diff --git a/docs/rh-log.txt b/docs/rh-log.txt index b1fe9e6b1..23c77631e 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,10 @@ January 9, 2008 +- Added texture packing to D3DFB so that textures that are temporally related + can share the same hardware texture. This greatly reduces the number of + DrawPrimitive calls that need to be made when drawing text (or any 2D + graphics in general), so now hardware text is much faster than software text + all around. (As an example, one scenario went from 315 fps to over 1635 fps + for hardware, compared to 540 fps for software.) - Fixed: The mouse was being grabbed in windowed mode again. - Modified M_DrawFrame() and R_DrawTopBorder() so that they call FlatFill() to draw the edges of the frames. This at least seems a bit faster for hardware diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 3480de270..c4dcc3b92 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -949,12 +949,12 @@ void FBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) con void FBaseStatusBar::RefreshBackground () const { - int x, x2, y, i, ratio; + int x, x2, y, ratio; if (SCREENWIDTH > 320) { ratio = CheckRatio (SCREENWIDTH, SCREENHEIGHT); - x = !(ratio & 3) || !Scaled ? ST_X : SCREENWIDTH*(48-BaseRatioSizes[ratio][3])/(48*2); + x = (!(ratio & 3) || !Scaled) ? ST_X : SCREENWIDTH*(48-BaseRatioSizes[ratio][3])/(48*2); if (x > 0) { y = x == ST_X ? ST_Y : ::ST_Y; @@ -966,15 +966,11 @@ void FBaseStatusBar::RefreshBackground () const if (setblocks >= 10) { const gameborder_t *border = gameinfo.border; + FTexture *p; - for (i = x - border->size; i > -border->size; i -= border->size) - { - screen->DrawTexture (TexMan[border->b], i, y, TAG_DONE); - } - for (i = x2; i < SCREENWIDTH; i += border->size) - { - screen->DrawTexture (TexMan[border->b], i, y, TAG_DONE); - } + p = TexMan[border->b]; + screen->FlatFill(0, y, x, y + p->GetHeight(), p, true); + screen->FlatFill(x2, y, SCREENWIDTH, y + p->GetHeight(), p, true); } } } diff --git a/src/r_defs.h b/src/r_defs.h index 00a719404..e8463420f 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -677,7 +677,7 @@ public: virtual FTextureFormat GetFormat(); // Returns a native 3D representation of the texture - FNativeTexture *GetNative(); + FNativeTexture *GetNative(bool wrapping); // Frees the native 3D representation of the texture void KillNative(); diff --git a/src/r_translate.cpp b/src/r_translate.cpp index ddd6906b3..39c186bbc 100644 --- a/src/r_translate.cpp +++ b/src/r_translate.cpp @@ -190,7 +190,7 @@ void FRemapTable::UpdateNative() } } -FNativeTexture *FRemapTable::GetNative() +FNativePalette *FRemapTable::GetNative() { if (Native == NULL) { diff --git a/src/r_translate.h b/src/r_translate.h index fe9e7b49b..b3711506b 100644 --- a/src/r_translate.h +++ b/src/r_translate.h @@ -33,7 +33,7 @@ struct FRemapTable void MakeIdentity(); void KillNative(); void UpdateNative(); - FNativeTexture *GetNative(); + FNativePalette *GetNative(); bool IsIdentity() const; void Serialize(FArchive &ar); void AddIndexRange(int start, int end, int pal1, int pal2); @@ -41,7 +41,7 @@ struct FRemapTable BYTE *Remap; // For the software renderer PalEntry *Palette; // The ideal palette this maps to - FNativeTexture *Native; // The Palette stored in a HW texture + FNativePalette *Native; // The Palette stored in a HW texture int NumEntries; // # of elements in this table (usually 256) private: diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index db5912fd6..35ebbf60b 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -405,17 +405,25 @@ void FTexture::FlipNonSquareBlockRemap (BYTE *dst, const BYTE *src, int x, int y } } -FNativeTexture *FTexture::GetNative() +FNativeTexture *FTexture::GetNative(bool wrapping) { if (Native != NULL) { - if (CheckModified()) + if (!Native->CheckWrapping(wrapping)) + { // Texture's wrapping mode is not compatible. + // Destroy it and get a new one. + delete Native; + } + else { - Native->Update(); + if (CheckModified()) + { + Native->Update(); + } + return Native; } - return Native; } - Native = screen->CreateTexture(this); + Native = screen->CreateTexture(this, wrapping); return Native; } diff --git a/src/v_video.cpp b/src/v_video.cpp index 6ad2b71e3..5f03c0fe3 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -844,12 +844,12 @@ bool DFrameBuffer::Begin2D (bool copy3d) return false; } -FNativeTexture *DFrameBuffer::CreateTexture(FTexture *gametex) +FNativeTexture *DFrameBuffer::CreateTexture(FTexture *gametex, bool wrapping) { return NULL; } -FNativeTexture *DFrameBuffer::CreatePalette(FRemapTable *remap) +FNativePalette *DFrameBuffer::CreatePalette(FRemapTable *remap) { return NULL; } @@ -1008,10 +1008,19 @@ void DFrameBuffer::CopyPixelData(BYTE * buffer, int texpitch, int texheight, int } } +FNativePalette::~FNativePalette() +{ +} + FNativeTexture::~FNativeTexture() { } +bool FNativeTexture::CheckWrapping(bool wrapping) +{ + return true; +} + CCMD(clean) { Printf ("CleanXfac: %d\nCleanYfac: %d\n", CleanXfac, CleanYfac); diff --git a/src/v_video.h b/src/v_video.h index b3885680c..e9cbc465e 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -268,11 +268,27 @@ protected: DSimpleCanvas() {} }; +// This class represents a native texture, as opposed to an FTexture. +class FNativeTexture +{ +public: + virtual ~FNativeTexture(); + virtual bool Update() = 0; + virtual bool CheckWrapping(bool wrapping); +}; + +// This class represents a texture lookup palette. +class FNativePalette +{ +public: + virtual ~FNativePalette(); + virtual bool Update() = 0; +}; + // A canvas that represents the actual display. The video code is responsible // for actually implementing this. Built on top of SimpleCanvas, because it // needs a system memory buffer when buffered output is enabled. -class FNativeTexture; class DFrameBuffer : public DSimpleCanvas { DECLARE_ABSTRACT_CLASS (DFrameBuffer, DSimpleCanvas) @@ -332,10 +348,10 @@ public: // DrawTexture calls after Begin2D use native textures. // Create a native texture from a game texture. - virtual FNativeTexture *CreateTexture(FTexture *gametex); + virtual FNativeTexture *CreateTexture(FTexture *gametex, bool wrapping); - // Create a palette texture from a palette. - virtual FNativeTexture *CreatePalette(FRemapTable *remap); + // Create a palette texture from a remap/palette table. + virtual FNativePalette *CreatePalette(FRemapTable *remap); // texture copy functions virtual void CopyPixelDataRGB(BYTE *buffer, int texpitch, int texheight, int originx, int originy, @@ -370,14 +386,6 @@ private: DWORD LastMS, LastSec, FrameCount, LastCount, LastTic; }; -// This class represents a native texture, as opposed to an FTexture. -class FNativeTexture -{ -public: - virtual ~FNativeTexture(); - virtual bool Update() = 0; -}; - extern FColorMatcher ColorMatcher; // This is the screen updated by I_FinishUpdate. diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index b5cf24700..c394f0eca 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -83,31 +83,62 @@ IMPLEMENT_CLASS(D3DFB) +struct D3DFB::PackedTexture +{ + D3DFB::PackingTexture *Owner; + + PackedTexture *Next, **Prev; + + // Pixels this image covers + RECT Area; + + // Texture coordinates for this image + float Left, Top, Right, Bottom; +}; + +struct D3DFB::PackingTexture +{ + PackingTexture(D3DFB *fb, int width, int height, D3DFORMAT format); + ~PackingTexture(); + + PackedTexture *GetBestFit(int width, int height, int &area); + void AllocateImage(PackedTexture *box, int width, int height); + PackedTexture *AllocateBox(); + void AddEmptyBox(int left, int top, int right, int bottom); + void FreeBox(PackedTexture *box); + + PackingTexture *Next; + IDirect3DTexture9 *Tex; + D3DFORMAT Format; + PackedTexture *UsedList; // Boxes that contain images + PackedTexture *EmptyList; // Boxes that contain empty space + PackedTexture *FreeList; // Boxes that are just waiting to be used + int Width, Height; + bool OneUse; +}; + class D3DTex : public FNativeTexture { public: - D3DTex(FTexture *tex, D3DFB *fb); + D3DTex(FTexture *tex, D3DFB *fb, bool wrapping); ~D3DTex(); FTexture *GameTex; - IDirect3DTexture9 *Tex; + D3DFB::PackedTexture *Box; D3DTex **Prev; D3DTex *Next; - // Texture coordinates to use for the lower-right corner, should this - // texture prove to be larger than the game texture it represents. - FLOAT TX, TY; - bool IsGray; - bool Create(IDirect3DDevice9 *D3DDevice); + bool Create(D3DFB *fb, bool wrapping); bool Update(); + bool CheckWrapping(bool wrapping); D3DFORMAT GetTexFormat(); FTextureFormat ToTexFmt(D3DFORMAT fmt); }; -class D3DPal : public FNativeTexture +class D3DPal : public FNativePalette { public: D3DPal(FRemapTable *remap, D3DFB *fb); @@ -181,6 +212,8 @@ CUSTOM_CVAR(Bool, test2d, true, CVAR_NOINITCALL) BorderNeedRefresh = SB_state = screen->GetPageCount(); } +CVAR(Int, d3d_showpacks, 0, 0) + // CODE -------------------------------------------------------------------- D3DFB::D3DFB (int width, int height, bool fullscreen) @@ -220,6 +253,7 @@ D3DFB::D3DFB (int width, int height, bool fullscreen) ScreenWipe = NULL; InScene = false; QuadExtra = new BufferedQuad[MAX_QUAD_BATCH]; + Packs = NULL; Gamma = 1.0; FlashColor0 = 0; @@ -326,6 +360,7 @@ void D3DFB::FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, b bool D3DFB::CreateResources () { + Packs = NULL; if (!Windowed) { // Remove the window border in fullscreen mode @@ -426,6 +461,12 @@ void D3DFB::ReleaseResources () delete ScreenWipe; ScreenWipe = NULL; } + PackingTexture *pack, *next; + for (pack = Packs; pack != NULL; pack = next) + { + next = pack->Next; + delete pack; + } GatheringWipeScreen = false; } @@ -747,6 +788,7 @@ void D3DFB::Update () if (InScene) { DrawRateStuff(); + DrawPackedTextures(d3d_showpacks); EndBatch(); // Make sure all batched primitives are drawn. DoWindowedGamma(); D3DDevice->EndScene(); @@ -1049,13 +1091,509 @@ void D3DFB::SetBlendingRect(int x1, int y1, int x2, int y2) /* 2D Stuff */ /**************************************************************************/ +//========================================================================== +// +// D3DFB :: DrawPackedTextures +// +// DEBUG: Draws the packing textures to the screen, starting with the +// 1-based packnum. +// +//========================================================================== + +void D3DFB::DrawPackedTextures(int packnum) +{ + D3DCOLOR empty_colors[8] = + { + 0xFFFF9999, 0xFF99FF99, 0xFF9999FF, 0xFFFFFF99, + 0xFFFF99FF, 0xFF99FFFF, 0xFFFFCC99, 0xFF99CCFF + }; + PackingTexture *pack; + int x = 8, y = 8; + + if (packnum <= 0) + { + return; + } + pack = Packs; + while (pack != NULL && pack->OneUse) + { // Skip textures that aren't used as packing containers + pack = pack->Next; + } + while (pack != NULL && packnum != 1) + { + if (!pack->OneUse) + { // Skip textures that aren't used as packing containers + packnum--; + } + pack = pack->Next; + } + while (pack != NULL) + { + if (pack->OneUse) + { // Skip textures that aren't used as packing containers + pack = pack->Next; + continue; + } + + AddColorOnlyQuad(x-1, y-1-LBOffsetI, 258, 258, D3DCOLOR_XRGB(255,255,0)); + + CheckQuadBatch(); + + BufferedQuad *quad = &QuadExtra[QuadBatchPos]; + FBVERTEX *vert = &VertexData[VertexPos]; + + if (pack->Format == D3DFMT_L8/* && !tex->IsGray*/) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette; + quad->ShaderNum = BQS_PalTex; + } + else + { + quad->Flags = BQF_WrapUV; + quad->ShaderNum = BQS_Plain; + } + quad->SrcBlend = 0; + quad->DestBlend = 0; + quad->Palette = NULL; + quad->Texture = pack; + + float x0 = float(x) - 0.5f; + float y0 = float(y) - 0.5f; + float x1 = x0 + 256.f; + float y1 = y0 + 256.f; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFFF; + vert[0].tu = 0; + vert[0].tv = 0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFFF; + vert[1].tu = 1; + vert[1].tv = 0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFFF; + vert[2].tu = 1; + vert[2].tv = 1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFFF; + vert[3].tu = 0; + vert[3].tv = 1; + + IndexData[IndexPos ] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; + + // Draw entries in the empty list. + PackedTexture *box; + int emptynum; + for (box = pack->EmptyList, emptynum = 0; box != NULL; box = box->Next, emptynum++) + { + AddColorOnlyQuad(x + box->Area.left, y + box->Area.top - LBOffsetI, + box->Area.right - box->Area.left, box->Area.bottom - box->Area.top, + empty_colors[emptynum & 7]); + } + + x += 256 + 8; + if (x > Width - 256) + { + x = 8; + y += 256 + 8; + if (y > TrueHeight - 256) + { + return; + } + } + pack = pack->Next; + } +} + +//========================================================================== +// +// D3DFB :: AllocPackedTexture +// +// Finds space to pack an image inside a packing texture and returns it. +// Large images and those that need to wrap always get their own textures. +// +//========================================================================== + +D3DFB::PackedTexture *D3DFB::AllocPackedTexture(int w, int h, bool wrapping, D3DFORMAT format) +{ + PackingTexture *bestpack; + PackedTexture *bestbox; + int area; + + if (w > 256 || h > 256 || wrapping) + { // Create a new packing texture. + bestpack = new PackingTexture(this, w, h, format); + bestpack->OneUse = true; + bestbox = bestpack->GetBestFit(w, h, area); + } + else + { // Try to find space in an existing packing texture. + int bestarea = INT_MAX; + int bestareaever = w * h; + bestpack = NULL; + bestbox = NULL; + for (PackingTexture *pack = Packs; pack != NULL; pack = pack->Next) + { + if (pack->Format == format) + { + PackedTexture *box = pack->GetBestFit(w, h, area); + if (area == bestareaever) + { // An exact fit! Use it! + bestpack = pack; + bestbox = box; + break; + } + if (area < bestarea) + { + bestarea = area; + bestpack = pack; + bestbox = box; + } + } + } + if (bestpack == NULL) + { // Create a new packing texture. + bestpack = new PackingTexture(this, 256, 256, format); + bestbox = bestpack->GetBestFit(w, h, bestarea); + } + } + bestpack->AllocateImage(bestbox, w, h); + return bestbox; +} + +//========================================================================== +// +// PackingTexture Constructor +// +//========================================================================== + +D3DFB::PackingTexture::PackingTexture(D3DFB *fb, int w, int h, D3DFORMAT format) +{ + Tex = NULL; + Format = format; + UsedList = NULL; + EmptyList = NULL; + FreeList = NULL; + OneUse = false; + Width = 0; + Height = 0; + + Next = fb->Packs; + fb->Packs = this; + +#if 1 + if (FAILED(fb->D3DDevice->CreateTexture(w, h, 1, 0, format, D3DPOOL_MANAGED, &Tex, NULL))) +#endif + { // Try again, using power-of-2 sizes + int i; + + for (i = 1; i < w; i <<= 1) {} w = i; + for (i = 1; i < h; i <<= 1) {} h = i; + if (FAILED(fb->D3DDevice->CreateTexture(w, h, 1, 0, format, D3DPOOL_MANAGED, &Tex, NULL))) + { + return; + } + } + Width = w; + Height = h; + + // The whole texture is initially empty. + AddEmptyBox(0, 0, w, h); +} + +//========================================================================== +// +// PackingTexture Destructor +// +//========================================================================== + +D3DFB::PackingTexture::~PackingTexture() +{ + PackedTexture *box, *next; + + SAFE_RELEASE( Tex ); + for (box = UsedList; box != NULL; box = next) + { + next = box->Next; + delete box; + } + for (box = EmptyList; box != NULL; box = next) + { + next = box->Next; + delete box; + } + for (box = FreeList; box != NULL; box = next) + { + next = box->Next; + delete box; + } +} + +//========================================================================== +// +// PackingTexture :: GetBestFit +// +// Returns the empty box that provides the best fit for the requested +// dimensions, or NULL if none of them are large enough. +// +//========================================================================== + +D3DFB::PackedTexture *D3DFB::PackingTexture::GetBestFit(int w, int h, int &area) +{ + PackedTexture *box; + int smallestarea = INT_MAX; + PackedTexture *smallestbox = NULL; + + for (box = EmptyList; box != NULL; box = box->Next) + { + int boxw = box->Area.right - box->Area.left; + int boxh = box->Area.bottom - box->Area.top; + if (boxw >= w && boxh >= h) + { + int boxarea = boxw * boxh; + if (boxarea < smallestarea) + { + smallestarea = boxarea; + smallestbox = box; + if (boxw == w && boxh == h) + { // An exact fit! Use it! + break; + } + } + } + } + area = smallestarea; + return smallestbox; +} + +//========================================================================== +// +// PackingTexture :: AllocateImage +// +// Moves the box from the empty list to the used list, sizing it to the +// requested dimensions and adding additional boxes to the empty list if +// needed. +// +// The passed box *MUST* be in this packing textures empty list. +// +//========================================================================== + +void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int h) +{ + RECT start = box->Area; + + box->Area.right = box->Area.left + w; + box->Area.bottom = box->Area.top + h; + + box->Left = float(box->Area.left) / Width; + box->Right = float(box->Area.right) / Width; + box->Top = float(box->Area.top) / Height; + box->Bottom = float(box->Area.bottom) / Height; + + // Remove it from the empty list. + *(box->Prev) = box->Next; + if (box->Next != NULL) + { + box->Next->Prev = box->Prev; + } + + // Add it to the used list. + box->Next = UsedList; + if (box->Next != NULL) + { + box->Next->Prev = &box->Next; + } + UsedList = box; + box->Prev = &UsedList; + + // If we didn't use the whole box, split the remainder into the empty list. +#if 1 + // Split like this: + // +---+------+ + // |###| | + // +---+------+ + // | | + // | | + // +----------+ + // Empirical evidence indicates that this gives the best utilization. + if (box->Area.bottom < start.bottom) + { + AddEmptyBox(start.left, box->Area.bottom, start.right, start.bottom); + } + if (box->Area.right < start.right) + { + AddEmptyBox(box->Area.right, start.top, start.right, box->Area.bottom); + } +#else + // Split like this: + // +---+------+ + // |###| | + // +---+ | + // | | | + // | | | + // +---+------+ + if (box->Area.bottom < start.bottom) + { + AddEmptyBox(start.left, box->Area.bottom, box->Area.right, start.bottom); + } + if (box->Area.right < start.right) + { + AddEmptyBox(box->Area.right, start.top, start.right, start.bottom); + } +#endif +} + +//========================================================================== +// +// PackingTexture :: AddEmptyBox +// +// Adds a box with the specified dimensions to the empty list. +// +//========================================================================== + +void D3DFB::PackingTexture::AddEmptyBox(int left, int top, int right, int bottom) +{ + PackedTexture *box = AllocateBox(); + box->Area.left = left; + box->Area.top = top; + box->Area.right = right; + box->Area.bottom = bottom; + box->Next = EmptyList; + if (box->Next != NULL) + { + box->Next->Prev = &box->Next; + } + box->Prev = &EmptyList; + EmptyList = box; +} + +//========================================================================== +// +// PackingTexture :: AllocateBox +// +// Returns a new PackedTexture box, either by retrieving one off the free +// list or by creating a new one. The box is not linked into a list. +// +//========================================================================== + +D3DFB::PackedTexture *D3DFB::PackingTexture::AllocateBox() +{ + PackedTexture *box; + + if (FreeList != NULL) + { + box = FreeList; + FreeList = box->Next; + if (box->Next != NULL) + { + box->Next->Prev = &FreeList; + } + } + else + { + box = new PackedTexture; + box->Owner = this; + } + return box; +} + +//========================================================================== +// +// PackingTexture :: FreeBox +// +// Removes a box from its current list and adds it to the empty list, +// updating EmptyArea. If there are no boxes left in the used list, then +// the empty list is replaced with a single box, so the texture can be +// subdivided again. +// +//========================================================================== + +void D3DFB::PackingTexture::FreeBox(D3DFB::PackedTexture *box) +{ + *(box->Prev) = box->Next; + if (box->Next != NULL) + { + box->Next->Prev = box->Prev; + } + box->Next = EmptyList; + box->Prev = &EmptyList; + if (EmptyList != NULL) + { + EmptyList->Prev = &box->Next; + } + EmptyList = box; + if (UsedList == NULL) + { // No more space in use! Move all but this into the free list. + if (box->Next != NULL) + { + D3DFB::PackedTexture *lastbox; + + // Find the last box in the free list. + lastbox = FreeList; + if (lastbox != NULL) + { + while (lastbox->Next != NULL) + { + lastbox = lastbox->Next; + } + } + // Chain the empty list to the end of the free list. + if (lastbox != NULL) + { + lastbox->Next = box->Next; + box->Next->Prev = &lastbox->Next; + } + else + { + FreeList = box->Next; + box->Next->Prev = &FreeList; + } + box->Next = NULL; + } + // Now this is the only box in the empty list, so it should + // contain the whole texture. + box->Area.left = 0; + box->Area.top = 0; + box->Area.right = Width; + box->Area.bottom = Height; + } +} + //========================================================================== // // D3DTex Constructor // //========================================================================== -D3DTex::D3DTex(FTexture *tex, D3DFB *fb) +D3DTex::D3DTex(FTexture *tex, D3DFB *fb, bool wrapping) { // Attach to the texture list for the D3DFB Next = fb->Textures; @@ -1067,10 +1605,10 @@ D3DTex::D3DTex(FTexture *tex, D3DFB *fb) fb->Textures = this; GameTex = tex; - Tex = NULL; + Box = NULL; IsGray = false; - Create(fb->D3DDevice); + Create(fb, wrapping); } //========================================================================== @@ -1081,7 +1619,11 @@ D3DTex::D3DTex(FTexture *tex, D3DFB *fb) D3DTex::~D3DTex() { - SAFE_RELEASE( Tex ); + if (Box != NULL) + { + Box->Owner->FreeBox(Box); + Box = NULL; + } // Detach from the texture list *Prev = Next; if (Next != NULL) @@ -1095,6 +1637,25 @@ D3DTex::~D3DTex() } } +//========================================================================== +// +// D3DTex :: CheckWrapping +// +// Returns true if the texture is compatible with the specified wrapping +// mode. +// +//========================================================================== + +bool D3DTex::CheckWrapping(bool wrapping) +{ + // If it doesn't need to wrap, then it works. + if (!wrapping) + { + return true; + } + // If it needs to wrap, then it can't be packed inside another texture. + return Box->Owner->OneUse; +} //========================================================================== // // D3DTex :: Create @@ -1104,46 +1665,23 @@ D3DTex::~D3DTex() // //========================================================================== -bool D3DTex::Create(IDirect3DDevice9 *D3DDevice) +bool D3DTex::Create(D3DFB *fb, bool wrapping) { - HRESULT hr; - int w, h; - - SAFE_RELEASE( Tex ); - - w = GameTex->GetWidth(); - h = GameTex->GetHeight(); - -#if 1 - hr = D3DDevice->CreateTexture(w, h, 1, 0, - GetTexFormat(), D3DPOOL_MANAGED, &Tex, NULL); -#else - hr = E_FAIL; -#endif - if (SUCCEEDED(hr)) + if (Box != NULL) { - TX = 1; - TY = 1; + Box->Owner->FreeBox(Box); } - else - { // Try again, using power-of-2 sizes - int i; - for (i = 1; i < w; i <<= 1) {} w = i; - for (i = 1; i < h; i <<= 1) {} h = i; - hr = D3DDevice->CreateTexture(w, h, 1, 0, - GetTexFormat(), D3DPOOL_MANAGED, &Tex, NULL); - if (FAILED(hr)) - { - return false; - } - TX = GameTex->GetWidth() / float(w); - TY = GameTex->GetHeight() / float(h); + Box = fb->AllocPackedTexture(GameTex->GetWidth(), GameTex->GetHeight(), wrapping, GetTexFormat()); + + if (Box == NULL) + { + return false; } if (!Update()) { - Tex->Release(); - Tex = NULL; + Box->Owner->FreeBox(Box); + Box = NULL; return false; } return true; @@ -1163,23 +1701,22 @@ bool D3DTex::Update() D3DLOCKED_RECT lrect; RECT rect; - assert(Tex != NULL); + assert(Box != NULL); + assert(Box->Owner != NULL); + assert(Box->Owner->Tex != NULL); assert(GameTex != NULL); - if (FAILED(Tex->GetLevelDesc(0, &desc))) + if (FAILED(Box->Owner->Tex->GetLevelDesc(0, &desc))) { return false; } - rect.left = 0; - rect.top = 0; - rect.right = GameTex->GetWidth(); - rect.bottom = GameTex->GetHeight(); - if (FAILED(Tex->LockRect(0, &lrect, &rect, 0))) + rect = Box->Area; + if (FAILED(Box->Owner->Tex->LockRect(0, &lrect, &rect, 0))) { return false; } - GameTex->FillBuffer((BYTE *)lrect.pBits, lrect.Pitch, rect.bottom, ToTexFmt(desc.Format)); - Tex->UnlockRect(0); + GameTex->FillBuffer((BYTE *)lrect.pBits, lrect.Pitch, GameTex->GetHeight(), ToTexFmt(desc.Format)); + Box->Owner->Tex->UnlockRect(0); return true; } @@ -1373,10 +1910,10 @@ bool D3DFB::Begin2D(bool copy3d) // //========================================================================== -FNativeTexture *D3DFB::CreateTexture(FTexture *gametex) +FNativeTexture *D3DFB::CreateTexture(FTexture *gametex, bool wrapping) { - D3DTex *tex = new D3DTex(gametex, this); - if (tex->Tex == NULL) + D3DTex *tex = new D3DTex(gametex, this, wrapping); + if (tex->Box == NULL) { delete tex; return NULL; @@ -1392,7 +1929,7 @@ FNativeTexture *D3DFB::CreateTexture(FTexture *gametex) // //========================================================================== -FNativeTexture *D3DFB::CreatePalette(FRemapTable *remap) +FNativePalette *D3DFB::CreatePalette(FRemapTable *remap) { D3DPal *tex = new D3DPal(remap, this); if (tex->Tex == NULL) @@ -1603,7 +2140,7 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi return; } - D3DTex *tex = static_cast(img->GetNative()); + D3DTex *tex = static_cast(img->GetNative(false)); if (tex == NULL) { @@ -1619,10 +2156,10 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi float y0 = float(parms.y) / 65536.f - float(parms.top) * yscale; float x1 = x0 + float(parms.destwidth) / 65536.f; float y1 = y0 + float(parms.destheight) / 65536.f; - float u0 = 0.f; - float v0 = 0.f; - float u1 = tex->TX; - float v1 = tex->TY; + float u0 = tex->Box->Left; + float v0 = tex->Box->Top; + float u1 = tex->Box->Right; + float v1 = tex->Box->Bottom; float uscale = 1.f / (parms.texwidth / u1); float vscale = 1.f / (parms.texheight / v1) / yscale; @@ -1666,7 +2203,7 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi return; } - QuadExtra[QuadBatchPos].Texture = tex; + QuadExtra[QuadBatchPos].Texture = tex->Box->Owner; if (parms.bilinear) { QuadExtra[QuadBatchPos].Flags |= BQF_Bilinear; @@ -1747,7 +2284,7 @@ void D3DFB::FlatFill(int left, int top, int right, int bottom, FTexture *src, bo { return; } - D3DTex *tex = static_cast(src->GetNative()); + D3DTex *tex = static_cast(src->GetNative(true)); if (tex == NULL) { return; @@ -1788,7 +2325,7 @@ void D3DFB::FlatFill(int left, int top, int right, int bottom, FTexture *src, bo quad->SrcBlend = 0; quad->DestBlend = 0; quad->Palette = NULL; - quad->Texture = tex; + quad->Texture = tex->Box->Owner; vert[0].x = x0; vert[0].y = y0; @@ -1835,7 +2372,8 @@ void D3DFB::FlatFill(int left, int top, int right, int bottom, FTexture *src, bo QuadBatchPos++; VertexPos += 4; - IndexPos += 6;} + IndexPos += 6; +} //========================================================================== // @@ -2384,7 +2922,7 @@ void D3DFB::SetPaletteTexture(IDirect3DTexture9 *texture, int count) SetTexture(1, texture); } -void D3DFB::SetPalTexBilinearConstants(D3DTex *tex) +void D3DFB::SetPalTexBilinearConstants(PackingTexture *tex) { float con[8]; @@ -2394,8 +2932,8 @@ void D3DFB::SetPalTexBilinearConstants(D3DTex *tex) return; } - con[0] = tex->GameTex->GetWidth() / tex->TX; - con[1] = tex->GameTex->GetHeight() / tex->TY; + con[0] = float(tex->Width); + con[1] = float(tex->Height); con[2] = 0; con[3] = 1 / con[0]; con[4] = 0; diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index 8f0725d98..5c7fdde00 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -239,8 +239,8 @@ public: void SetVSync (bool vsync); void SetBlendingRect (int x1, int y1, int x2, int y2); bool Begin2D (bool copy3d); - FNativeTexture *CreateTexture (FTexture *gametex); - FNativeTexture *CreatePalette (FRemapTable *remap); + FNativeTexture *CreateTexture (FTexture *gametex, bool wrapping); + FNativePalette *CreatePalette (FRemapTable *remap); void STACK_ARGS DrawTextureV (FTexture *img, int x, int y, uint32 tag, va_list tags); void Clear (int left, int top, int right, int bottom, int palcolor, uint32 color); void Dim (PalEntry color, float amount, int x1, int y1, int w, int h); @@ -257,6 +257,9 @@ private: friend class D3DTex; friend class D3DPal; + struct PackedTexture; + struct PackingTexture; + struct FBVERTEX { FLOAT x, y, z, rhw; @@ -278,7 +281,7 @@ private: DWORD Group1; }; D3DPal *Palette; - D3DTex *Texture; + PackingTexture *Texture; }; void SetInitialState(); @@ -297,6 +300,8 @@ private: void ReleaseDefaultPoolItems(); void KillNativePals(); void KillNativeTexs(); + PackedTexture *AllocPackedTexture(int width, int height, bool wrapping, D3DFORMAT format); + void DrawPackedTextures(int packnum); void DrawLetterbox(); void Draw3DPart(bool copy3d); bool SetStyle(D3DTex *tex, DCanvas::DrawParms &parms, D3DCOLOR &color0, D3DCOLOR &color1, BufferedQuad &quad); @@ -316,7 +321,7 @@ private: void SetPixelShader(IDirect3DPixelShader9 *shader); void SetTexture(int tnum, IDirect3DTexture9 *texture); void SetPaletteTexture(IDirect3DTexture9 *texture, int count); - void SetPalTexBilinearConstants(D3DTex *texture); + void SetPalTexBilinearConstants(PackingTexture *texture); BOOL AlphaBlendEnabled; D3DBLEND AlphaSrcBlend; @@ -347,6 +352,7 @@ private: bool GatheringWipeScreen; D3DPal *Palettes; D3DTex *Textures; + PackingTexture *Packs; IDirect3DDevice9 *D3DDevice; IDirect3DTexture9 *FBTexture;