diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index f5123826ae..eee1a0027a 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -662,11 +662,16 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); } - MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1); + Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1); + if (pixelsTex) { + draw_->BindTextures(0, 1, &pixelsTex); - Bind2DShader(); - DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); - gpuStats.numUploads++; + Bind2DShader(); + DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); + gpuStats.numUploads++; + + pixelsTex->Release(); + } } void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags) { @@ -701,7 +706,10 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu float u0 = 0.0f, u1 = 480.0f / 512.0f; float v0 = 0.0f, v1 = 1.0f; - MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272, u1, v1); + Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272, u1, v1); + if (!pixelsTex) + return; + draw_->BindTextures(0, 1, &pixelsTex); int uvRotation = useBufferedRendering_ ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL; // TODO: Currently we can't access the texture from Draw. @@ -712,6 +720,9 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu Bind2DShader(); + if (needBackBufferYSwap_) + std::swap(v0, v1); + DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST; flags = flags | DRAWTEX_TO_BACKBUFFER; SetViewport2D(0, 0, pixelWidth_, pixelHeight_); @@ -735,6 +746,8 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1, uniforms); }*/ + pixelsTex->Release(); + // PresentationCommon sets all kinds of state, we can't rely on anything. gstate_c.Dirty(DIRTY_ALL); } diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 6f89fb0db5..aad0bf8e7e 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -138,7 +138,6 @@ enum BindFramebufferColorFlags { enum DrawTextureFlags { DRAWTEX_NEAREST = 0, DRAWTEX_LINEAR = 1, - DRAWTEX_KEEP_TEX = 2, DRAWTEX_KEEP_STENCIL_ALPHA = 4, DRAWTEX_TO_BACKBUFFER = 8, }; @@ -309,7 +308,7 @@ public: protected: virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h); void SetViewport2D(int x, int y, int w, int h); - virtual void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) = 0; + virtual Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) = 0; virtual void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) = 0; virtual void Bind2DShader() = 0; diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 5098a4c114..5a1fda5536 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -143,14 +143,6 @@ FramebufferManagerD3D11::~FramebufferManagerD3D11() { quadBuffer_->Release(); fsQuadBuffer_->Release(); - if (drawPixelsTex_) - drawPixelsTex_->Release(); - if (drawPixelsTexView_) - drawPixelsTexView_->Release(); - - // Temp FBOs cleared by FramebufferCommon. - delete[] convBuf; - // Stencil cleanup for (int i = 0; i < 256; i++) { if (stencilMaskStates_[i]) @@ -186,69 +178,52 @@ void FramebufferManagerD3D11::SetDrawEngine(DrawEngineD3D11 *td) { drawEngine_ = td; } -void FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - // TODO: Check / use D3DCAPS2_DYNAMICTEXTURES? - if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) { - drawPixelsTex_->Release(); - drawPixelsTex_ = nullptr; - drawPixelsTexView_->Release(); - drawPixelsTexView_ = nullptr; - } +Draw::Texture *FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { + auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) { + for (int y = 0; y < height; y++) { + const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; + const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)(data + byteStride * y); + switch (srcPixelFormat) { + case GE_FORMAT_565: + ConvertRGB565ToBGRA8888(dst, src16, width); + break; - if (!drawPixelsTex_) { - int usage = 0; - D3D11_TEXTURE2D_DESC desc{}; - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - desc.Width = width; - desc.Height = height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.SampleDesc.Count = 1; - desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &drawPixelsTex_)); - ASSERT_SUCCESS(device_->CreateShaderResourceView(drawPixelsTex_, nullptr, &drawPixelsTexView_)); - drawPixelsTexW_ = width; - drawPixelsTexH_ = height; - } + case GE_FORMAT_5551: + ConvertRGBA5551ToBGRA8888(dst, src16, width); + break; - if (!drawPixelsTex_) { - return; - } + case GE_FORMAT_4444: + ConvertRGBA4444ToBGRA8888(dst, src16, width); + break; - D3D11_MAPPED_SUBRESOURCE map; - context_->Map(drawPixelsTex_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + case GE_FORMAT_8888: + ConvertRGBA8888ToBGRA8888(dst, src32, width); + break; - for (int y = 0; y < height; y++) { - const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; - const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)((u8 *)map.pData + map.RowPitch * y); - switch (srcPixelFormat) { - case GE_FORMAT_565: - ConvertRGB565ToBGRA8888(dst, src16, width); - break; - - case GE_FORMAT_5551: - ConvertRGBA5551ToBGRA8888(dst, src16, width); - break; - - case GE_FORMAT_4444: - ConvertRGBA4444ToBGRA8888(dst, src16, width); - break; - - case GE_FORMAT_8888: - ConvertRGBA8888ToBGRA8888(dst, src32, width); - break; - - case GE_FORMAT_INVALID: - _dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels()."); - break; + case GE_FORMAT_INVALID: + _dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels()."); + break; + } } - } + }; - context_->Unmap(drawPixelsTex_, 0); - context_->PSSetShaderResources(0, 1, &drawPixelsTexView_); + Draw::TextureDesc desc{ + Draw::TextureType::LINEAR2D, + Draw::DataFormat::B8G8R8A8_UNORM, + width, + height, + 1, + 1, + false, + "DrawPixels", + { (uint8_t *)srcPixels }, + generateTexture, + }; + Draw::Texture *tex = draw_->CreateTexture(desc); + if (!tex) + ERROR_LOG(G3D, "Failed to create drawpixels texture"); + return tex; } void FramebufferManagerD3D11::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.h b/GPU/D3D11/FramebufferManagerD3D11.h index ef86098ae3..dc50926b16 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.h +++ b/GPU/D3D11/FramebufferManagerD3D11.h @@ -68,7 +68,7 @@ protected: private: void Bind2DShader() override; - void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; + Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); void SimpleBlit( Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, @@ -79,12 +79,6 @@ private: ID3D11DeviceContext *context_; D3D_FEATURE_LEVEL featureLevel_; - // Used by DrawPixels - ID3D11Texture2D *drawPixelsTex_ = nullptr; - ID3D11ShaderResourceView *drawPixelsTexView_ = nullptr; - int drawPixelsTexW_ = 0; - int drawPixelsTexH_ = 0; - ID3D11VertexShader *quadVertexShader_; ID3D11PixelShader *quadPixelShader_; ID3D11InputLayout *quadInputLayout_; @@ -95,8 +89,6 @@ private: const UINT quadOffset_ = 0; static const D3D11_INPUT_ELEMENT_DESC g_QuadVertexElements[2]; - u8 *convBuf = nullptr; - ID3D11PixelShader *stencilUploadPS_ = nullptr; ID3D11VertexShader *stencilUploadVS_ = nullptr; ID3D11InputLayout *stencilUploadInputLayout_ = nullptr; diff --git a/GPU/D3D11/StencilBufferD3D11.cpp b/GPU/D3D11/StencilBufferD3D11.cpp index 7c9e791cca..ad9faa7f0a 100644 --- a/GPU/D3D11/StencilBufferD3D11.cpp +++ b/GPU/D3D11/StencilBufferD3D11.cpp @@ -160,7 +160,9 @@ bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZ u16 h = dstBuffer->renderHeight; float u1 = 1.0f; float v1 = 1.0f; - MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); + Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); + if (!tex) + return false; if (dstBuffer->fbo) { draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); } else { @@ -189,7 +191,7 @@ bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZ context_->IASetInputLayout(stencilUploadInputLayout_); context_->PSSetShader(stencilUploadPS_, nullptr, 0); context_->VSSetShader(stencilUploadVS_, nullptr, 0); - context_->PSSetShaderResources(0, 1, &drawPixelsTexView_); + draw_->BindTextures(0, 1, &tex); context_->RSSetState(stockD3D11.rasterStateNoCull); context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); context_->IASetVertexBuffers(0, 1, &quadBuffer_, &quadStride_, &quadOffset_); @@ -237,6 +239,8 @@ bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZ context_->PSSetConstantBuffers(0, 1, &stencilValueBuffer_); context_->Draw(4, 0); } + + tex->Release(); RebindFramebuffer(); return true; } diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index a0c58c3e61..fcc06476c2 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -131,13 +131,9 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { pFramebufferPixelShader = nullptr; } pFramebufferVertexDecl->Release(); - if (drawPixelsTex_) { - drawPixelsTex_->Release(); - } for (auto &it : offscreenSurfaces_) { it.second.surface->Release(); } - delete [] convBuf; if (stencilUploadPS_) { stencilUploadPS_->Release(); } @@ -163,87 +159,52 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = { drawEngine_ = td; } - void FramebufferManagerDX9::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - u8 *convBuf = NULL; - D3DLOCKED_RECT rect; - - // TODO: Check / use D3DCAPS2_DYNAMICTEXTURES? - if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) { - drawPixelsTex_->Release(); - drawPixelsTex_ = nullptr; - } - - if (!drawPixelsTex_) { - int usage = 0; - D3DPOOL pool = D3DPOOL_MANAGED; - if (deviceEx_) { - pool = D3DPOOL_DEFAULT; - usage = D3DUSAGE_DYNAMIC; - } - HRESULT hr = device_->CreateTexture(width, height, 1, usage, D3DFMT_A8R8G8B8, pool, &drawPixelsTex_, NULL); - if (FAILED(hr)) { - drawPixelsTex_ = nullptr; - ERROR_LOG(G3D, "Failed to create drawpixels texture"); - } - drawPixelsTexW_ = width; - drawPixelsTexH_ = height; - } - - if (!drawPixelsTex_) { - return; - } - - drawPixelsTex_->LockRect(0, &rect, NULL, D3DLOCK_DISCARD); - - convBuf = (u8*)rect.pBits; - - // Final format is BGRA(directx) - if (srcPixelFormat != GE_FORMAT_8888 || srcStride != 512) { + Draw::Texture *FramebufferManagerDX9::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { + auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) { for (int y = 0; y < height; y++) { + const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; + const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)(data + byteStride * y); switch (srcPixelFormat) { case GE_FORMAT_565: - { - const u16_le *src = (const u16_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + rect.Pitch * y); - ConvertRGB565ToBGRA8888(dst, src, width); - } + ConvertRGB565ToBGRA8888(dst, src16, width); break; - // faster + case GE_FORMAT_5551: - { - const u16_le *src = (const u16_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + rect.Pitch * y); - ConvertRGBA5551ToBGRA8888(dst, src, width); - } + ConvertRGBA5551ToBGRA8888(dst, src16, width); break; + case GE_FORMAT_4444: - { - const u16_le *src = (const u16_le *)srcPixels + srcStride * y; - u8 *dst = (u8 *)(convBuf + rect.Pitch * y); - ConvertRGBA4444ToBGRA8888((u32 *)dst, src, width); - } + ConvertRGBA4444ToBGRA8888(dst, src16, width); break; case GE_FORMAT_8888: - { - const u32_le *src = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + rect.Pitch * y); - ConvertRGBA8888ToBGRA8888(dst, src, width); - } + ConvertRGBA8888ToBGRA8888(dst, src32, width); + break; + + case GE_FORMAT_INVALID: + _dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels()."); break; } } - } else { - for (int y = 0; y < height; y++) { - const u32_le *src = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)(convBuf + rect.Pitch * y); - ConvertRGBA8888ToBGRA8888(dst, src, width); - } - } + }; - drawPixelsTex_->UnlockRect(0); - device_->SetTexture(0, drawPixelsTex_); - // D3DXSaveTextureToFile("game:\\cc.png", D3DXIFF_PNG, drawPixelsTex_, NULL); + Draw::TextureDesc desc{ + Draw::TextureType::LINEAR2D, + Draw::DataFormat::B8G8R8A8_UNORM, + width, + height, + 1, + 1, + false, + "DrawPixels", + { (uint8_t *)srcPixels }, + generateTexture, + }; + Draw::Texture *tex = draw_->CreateTexture(desc); + if (!tex) + ERROR_LOG(G3D, "Failed to create drawpixels texture"); + return tex; } void FramebufferManagerDX9::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index 1ab874caf5..d270345820 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -27,7 +27,6 @@ #include "GPU/GPUCommon.h" #include "GPU/Common/FramebufferCommon.h" -#include "ext/native/thin3d/thin3d.h" namespace DX9 { @@ -77,7 +76,7 @@ protected: void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override; private: - void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; + Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override; void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h); bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer); @@ -85,17 +84,10 @@ private: LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9 deviceEx_; - // Used by DrawPixels - LPDIRECT3DTEXTURE9 drawPixelsTex_ = nullptr; - int drawPixelsTexW_; - int drawPixelsTexH_; - LPDIRECT3DVERTEXSHADER9 pFramebufferVertexShader = nullptr; LPDIRECT3DPIXELSHADER9 pFramebufferPixelShader = nullptr; LPDIRECT3DVERTEXDECLARATION9 pFramebufferVertexDecl = nullptr; - u8 *convBuf = nullptr; - LPDIRECT3DPIXELSHADER9 stencilUploadPS_ = nullptr; LPDIRECT3DVERTEXSHADER9 stencilUploadVS_ = nullptr; bool stencilUploadFailed_ = false; diff --git a/GPU/Directx9/StencilBufferDX9.cpp b/GPU/Directx9/StencilBufferDX9.cpp index 49c73c2821..1219c4e334 100644 --- a/GPU/Directx9/StencilBufferDX9.cpp +++ b/GPU/Directx9/StencilBufferDX9.cpp @@ -201,7 +201,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer float u1 = 1.0f; float v1 = 1.0f; - MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); + Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); device_->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_STENCIL, D3DCOLOR_RGBA(0, 0, 0, 0), 0.0f, 0); @@ -220,7 +220,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer device_->SetPixelShader(stencilUploadPS_); device_->SetVertexShader(stencilUploadVS_); - device_->SetTexture(0, drawPixelsTex_); + draw_->BindTextures(0, 1, &tex); shaderManagerDX9_->DirtyLastShader(); textureCacheDX9_->ForgetLastTexture(); @@ -248,6 +248,8 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer ERROR_LOG_REPORT(G3D, "Failed to draw stencil bit %x: %08x", i, hr); } } + + tex->Release(); dxstate.stencilMask.set(0xFF); dxstate.viewport.restore(); RebindFramebuffer(); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index de34febdc6..793e54726e 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -150,10 +150,6 @@ void FramebufferManagerGLES::DestroyDeviceObjects() { render_->DeleteProgram(draw2dprogram_); draw2dprogram_ = nullptr; } - if (drawPixelsTex_) { - render_->DeleteTexture(drawPixelsTex_); - drawPixelsTex_ = 0; - } if (stencilUploadProgram_) { render_->DeleteProgram(stencilUploadProgram_); stencilUploadProgram_ = nullptr; @@ -170,52 +166,54 @@ FramebufferManagerGLES::~FramebufferManagerGLES() { delete [] convBuf_; } -void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - if (drawPixelsTex_) { - render_->DeleteTexture(drawPixelsTex_); - } - - drawPixelsTex_ = render_->CreateTexture(GL_TEXTURE_2D); - drawPixelsTexW_ = width; - drawPixelsTexH_ = height; - - drawPixelsTexFormat_ = srcPixelFormat; - +Draw::Texture *FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { // TODO: We can just change the texture format and flip some bits around instead of this. // Could share code with the texture cache perhaps. - u32 neededSize = width * height * 4; - u8 *convBuf = new u8[neededSize]; - for (int y = 0; y < height; y++) { - const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; - const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)convBuf + width * y; - switch (srcPixelFormat) { - case GE_FORMAT_565: - ConvertRGBA565ToRGBA8888((u32 *)dst, src16, width); - break; + auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) { + for (int y = 0; y < height; y++) { + const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; + const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; + u32 *dst = (u32 *)(data + byteStride * y); + switch (srcPixelFormat) { + case GE_FORMAT_565: + ConvertRGBA565ToRGBA8888(dst, src16, width); + break; - case GE_FORMAT_5551: - ConvertRGBA5551ToRGBA8888((u32 *)dst, src16, width); - break; + case GE_FORMAT_5551: + ConvertRGBA5551ToRGBA8888(dst, src16, width); + break; - case GE_FORMAT_4444: - ConvertRGBA4444ToRGBA8888((u32 *)dst, src16, width); - break; + case GE_FORMAT_4444: + ConvertRGBA4444ToRGBA8888(dst, src16, width); + break; - case GE_FORMAT_8888: - memcpy(dst, src32, 4 * width); - break; + case GE_FORMAT_8888: + memcpy(dst, src32, 4 * width); + break; - case GE_FORMAT_INVALID: - _dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels()."); - break; + case GE_FORMAT_INVALID: + _dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels()."); + break; + } } - } - render_->TextureImage(drawPixelsTex_, 0, width, height, Draw::DataFormat::R8G8B8A8_UNORM, convBuf, GLRAllocType::NEW, false); - render_->FinalizeTexture(drawPixelsTex_, 0, false); + }; - // TODO: Return instead? - render_->BindTexture(TEX_SLOT_PSP_TEXTURE, drawPixelsTex_); + Draw::TextureDesc desc{ + Draw::TextureType::LINEAR2D, + Draw::DataFormat::R8G8B8A8_UNORM, + width, + height, + 1, + 1, + false, + "DrawPixels", + { (uint8_t *)srcPixels }, + generateTexture, + }; + Draw::Texture *tex = draw_->CreateTexture(desc); + if (!tex) + ERROR_LOG(G3D, "Failed to create drawpixels texture"); + return tex; } // x, y, w, h are relative coordinates against destW/destH, which is not very intuitive. diff --git a/GPU/GLES/FramebufferManagerGLES.h b/GPU/GLES/FramebufferManagerGLES.h index 05a6ae366f..bfb438cbc3 100644 --- a/GPU/GLES/FramebufferManagerGLES.h +++ b/GPU/GLES/FramebufferManagerGLES.h @@ -73,7 +73,7 @@ private: void CreateDeviceObjects(); void DestroyDeviceObjects(); - void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; + Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; void Bind2DShader() override; void CompileDraw2DProgram(); @@ -81,14 +81,9 @@ private: GLRenderManager *render_; - // Used by DrawPixels - GLRTexture *drawPixelsTex_ = nullptr; - GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID; - int drawPixelsTexW_ = 0; - int drawPixelsTexH_ = 0; - u8 *convBuf_ = nullptr; u32 convBufSize_ = 0; + GLRProgram *draw2dprogram_ = nullptr; GLRProgram *stencilUploadProgram_ = nullptr; diff --git a/GPU/GLES/StencilBufferGLES.cpp b/GPU/GLES/StencilBufferGLES.cpp index 76239b5282..84e603be2b 100644 --- a/GPU/GLES/StencilBufferGLES.cpp +++ b/GPU/GLES/StencilBufferGLES.cpp @@ -177,8 +177,9 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe float u1 = 1.0f; float v1 = 1.0f; - MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height, u1, v1); + Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height, u1, v1); textureCacheGL_->ForgetLastTexture(); + draw_->BindTextures(TEX_SLOT_PSP_TEXTURE, 1, &tex); // We must bind the program after starting the render pass, and set the color mask after clearing. render_->SetScissor({ 0, 0, w, h }); @@ -211,6 +212,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST); } + tex->Release(); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE); RebindFramebuffer(); return true; diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 19ef7a48ea..ea3a7d323c 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -270,7 +270,6 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) { outputFlags |= OutputFlags::BACKBUFFER_FLIPPED; } - // TODO, also deal with RB swizzle. PostShaderUniforms uniforms{}; presentation_->CalculatePostShaderUniforms(desc.width, desc.height, false, &uniforms); diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 347bd63369..8db0df9438 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -85,8 +85,6 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk } FramebufferManagerVulkan::~FramebufferManagerVulkan() { - delete[] convBuf_; - DeviceLost(); } @@ -127,9 +125,6 @@ void FramebufferManagerVulkan::InitDeviceObjects() { } void FramebufferManagerVulkan::DestroyDeviceObjects() { - delete drawPixelsTex_; - drawPixelsTex_ = nullptr; - if (fsBasicTex_ != VK_NULL_HANDLE) { vulkan2D_->PurgeFragmentShader(fsBasicTex_); vulkan_->Delete().QueueDeleteShaderModule(fsBasicTex_); @@ -181,57 +176,23 @@ void FramebufferManagerVulkan::Init() { Resized(); } -void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { - if (drawPixelsTex_) { - delete drawPixelsTex_; - drawPixelsTex_ = nullptr; - } - - VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); - - // There's only ever a few of these alive, don't need to stress the allocator with these big ones. - // OR NOT! Hot Shot Golf (#12355) does tons of these in a frame in some situations! So actually, - // we do use an allocator. In fact, I've now banned allocator-less textures. - - drawPixelsTex_ = new VulkanTexture(vulkan_); - drawPixelsTex_->SetTag("DrawPixels"); - if (!drawPixelsTex_->CreateDirect(initCmd, allocator_, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)) { - // out of memory? - delete drawPixelsTex_; - drawPixelsTex_ = nullptr; - overrideImageView_ = VK_NULL_HANDLE; - return; - } - // Initialize backbuffer texture for DrawPixels - drawPixelsTexFormat_ = srcPixelFormat; - - // TODO: We can just change the texture format and flip some bits around instead of this. - // Could share code with the texture cache perhaps. - // TODO: Could also convert directly into the pushbuffer easily. - const uint8_t *data = srcPixels; - if (srcPixelFormat != GE_FORMAT_8888 || srcStride != width) { - u32 neededSize = width * height * 4; - if (!convBuf_ || convBufSize_ < neededSize) { - delete[] convBuf_; - convBuf_ = new u8[neededSize]; - convBufSize_ = neededSize; - } - data = convBuf_; +Draw::Texture *FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) { + auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) { for (int y = 0; y < height; y++) { const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; - u32 *dst = (u32 *)convBuf_ + width * y; + u32 *dst = (u32 *)(data + byteStride * y); switch (srcPixelFormat) { case GE_FORMAT_565: - ConvertRGBA565ToRGBA8888((u32 *)dst, src16, width); + ConvertRGBA565ToRGBA8888(dst, src16, width); break; case GE_FORMAT_5551: - ConvertRGBA5551ToRGBA8888((u32 *)dst, src16, width); + ConvertRGBA5551ToRGBA8888(dst, src16, width); break; case GE_FORMAT_4444: - ConvertRGBA4444ToRGBA8888((u32 *)dst, src16, width); + ConvertRGBA4444ToRGBA8888(dst, src16, width); break; case GE_FORMAT_8888: @@ -243,14 +204,28 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor break; } } - } + }; - VkBuffer buffer; - size_t offset = push_->Push(data, width * height * 4, &buffer); - drawPixelsTex_->UploadMip(initCmd, 0, width, height, buffer, (uint32_t)offset, width); - drawPixelsTex_->EndCreate(initCmd); + // Hot Shot Golf (#12355) does tons of these in a frame in some situations! So actually, + // we do use an allocator. In fact, I've now banned allocator-less textures. - overrideImageView_ = drawPixelsTex_->GetImageView(); + Draw::TextureDesc desc{ + Draw::TextureType::LINEAR2D, + Draw::DataFormat::R8G8B8A8_UNORM, + width, + height, + 1, + 1, + false, + "DrawPixels", + { (uint8_t *)srcPixels }, + generateTexture, + }; + // TODO: Use allocator_ somehow? + Draw::Texture *tex = draw_->CreateTexture(desc); + if (!tex) + ERROR_LOG(G3D, "Failed to create drawpixels texture"); + return tex; } void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) { @@ -305,9 +280,7 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); - if ((flags & DRAWTEX_KEEP_TEX) == 0) - overrideImageView_ = VK_NULL_HANDLE; + VkImageView view = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); VkBuffer vbuffer; VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer); diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 25d44caf31..45f8c305e2 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -79,7 +79,7 @@ protected: private: // The returned texture does not need to be free'd, might be returned from a pool (currently single entry) - void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; + Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override; void InitDeviceObjects(); void DestroyDeviceObjects(); @@ -88,12 +88,6 @@ private: // Used to keep track of command buffers here but have moved all that into Thin3D. - // Used by DrawPixels - VulkanTexture *drawPixelsTex_ = nullptr; - GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID; - u8 *convBuf_ = nullptr; - u32 convBufSize_ = 0; - TextureCacheVulkan *textureCacheVulkan_ = nullptr; ShaderManagerVulkan *shaderManagerVulkan_ = nullptr; DrawEngineVulkan *drawEngineVulkan_ = nullptr; @@ -118,9 +112,6 @@ private: VkSampler linearSampler_; VkSampler nearestSampler_; - // hack! - VkImageView overrideImageView_ = VK_NULL_HANDLE; - // Simple 2D drawing engine. Vulkan2D *vulkan2D_; }; diff --git a/GPU/Vulkan/StencilBufferVulkan.cpp b/GPU/Vulkan/StencilBufferVulkan.cpp index cfce98f3fb..76a79d692b 100644 --- a/GPU/Vulkan/StencilBufferVulkan.cpp +++ b/GPU/Vulkan/StencilBufferVulkan.cpp @@ -163,7 +163,7 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip u16 h = dstBuffer->renderHeight; float u1 = 1.0f; float v1 = 1.0f; - MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); + Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1); if (dstBuffer->fbo) { draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }); } else { @@ -176,7 +176,9 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip renderManager->SetScissor({ { 0, 0, },{ (uint32_t)w, (uint32_t)h } }); gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE); - VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(overrideImageView_, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); + draw_->BindTextures(0, 1, &tex); + VkImageView drawPixelsImageView = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW); + VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(drawPixelsImageView, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE); // Note: Even with skipZero, we don't necessarily start framebuffers at 0 in Vulkan. Clear anyway. // Not an actual clear, because we need to draw to alpha only as well. @@ -212,7 +214,7 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, VK_NULL_HANDLE, 0, 3); // full screen triangle } - overrideImageView_ = VK_NULL_HANDLE; + tex->Release(); RebindFramebuffer(); return true; } diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 55fa59438c..39b74c7fe0 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -8,8 +8,9 @@ #include #include -#include +#include #include +#include #include "base/logging.h" #include "DataFormat.h" @@ -519,6 +520,10 @@ struct DeviceCaps { std::string deviceName; // The device name to use when creating the thin3d context, to get the same one. }; +// Use to write data directly to memory. initData is the pointer passed in TextureDesc. +// Important: only write to the provided pointer, don't read from it. +typedef std::function TextureCallback; + struct TextureDesc { TextureType type; DataFormat format; @@ -530,7 +535,8 @@ struct TextureDesc { // Optional, for tracking memory usage. std::string tag; // Does not take ownership over pointed-to data. - std::vector initData; + std::vector initData; + TextureCallback initDataCallback; }; enum class RPAction { diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 9db30a784f..fd7f07f47e 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -689,16 +689,18 @@ public: ~D3D11Texture() { if (tex) tex->Release(); + if (stagingTex) + stagingTex->Release(); if (view) view->Release(); } ID3D11Texture2D *tex = nullptr; + ID3D11Texture2D *stagingTex = nullptr; ID3D11ShaderResourceView *view = nullptr; }; Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { - if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) { // D3D11 does not support this format as a texture format. return nullptr; @@ -720,25 +722,45 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { descColor.Format = dataFormatToD3D11(desc.format); descColor.SampleDesc.Count = 1; descColor.SampleDesc.Quality = 0; + + if (desc.initDataCallback) { + descColor.Usage = D3D11_USAGE_STAGING; + descColor.BindFlags = 0; + descColor.MiscFlags = 0; + descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + HRESULT hr = device_->CreateTexture2D(&descColor, nullptr, &tex->stagingTex); + if (!SUCCEEDED(hr)) { + delete tex; + return nullptr; + } + } + descColor.Usage = D3D11_USAGE_DEFAULT; descColor.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE; descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0; descColor.CPUAccessFlags = 0; + D3D11_SUBRESOURCE_DATA *initDataParam = nullptr; D3D11_SUBRESOURCE_DATA initData[12]{}; - if (desc.initData.size() && !generateMips) { + std::vector initDataBuffer[12]; + if (desc.initData.size() && !generateMips && !desc.initDataCallback) { int w = desc.width; int h = desc.height; + int d = desc.depth; for (int i = 0; i < (int)desc.initData.size(); i++) { + uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(desc.format); initData[i].pSysMem = desc.initData[i]; - initData[i].SysMemPitch = (UINT)(w * DataFormatSizeInBytes(desc.format)); - initData[i].SysMemSlicePitch = (UINT)(w * h * DataFormatSizeInBytes(desc.format)); - w /= 2; - h /= 2; + initData[i].SysMemPitch = (UINT)byteStride; + initData[i].SysMemSlicePitch = (UINT)(h * byteStride); + w = (w + 1) / 2; + h = (h + 1) / 2; + d = (d + 1) / 2; } + initDataParam = initData; } - HRESULT hr = device_->CreateTexture2D(&descColor, (desc.initData.size() && !generateMips) ? initData : nullptr, &tex->tex); + HRESULT hr = device_->CreateTexture2D(&descColor, initDataParam, &tex->tex); if (!SUCCEEDED(hr)) { delete tex; return nullptr; @@ -749,8 +771,52 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) { return nullptr; } if (generateMips && desc.initData.size() >= 1) { - context_->UpdateSubresource(tex->tex, 0, nullptr, desc.initData[0], desc.width * (int)DataFormatSizeInBytes(desc.format), 0); + if (desc.initDataCallback) { + D3D11_MAPPED_SUBRESOURCE mapped; + hr = context_->Map(tex->stagingTex, 0, D3D11_MAP_WRITE, 0, &mapped); + if (!SUCCEEDED(hr)) { + delete tex; + return nullptr; + } + + desc.initDataCallback((uint8_t *)mapped.pData, desc.initData[0], desc.width, desc.height, desc.depth, mapped.RowPitch, mapped.DepthPitch); + context_->Unmap(tex->tex, 0); + + context_->CopyResource(tex->stagingTex, tex->stagingTex); + tex->stagingTex->Release(); + tex->stagingTex = nullptr; + } else { + uint32_t byteStride = desc.width * (uint32_t)DataFormatSizeInBytes(desc.format); + context_->UpdateSubresource(tex->tex, 0, nullptr, desc.initData[0], byteStride, 0); + } context_->GenerateMips(tex->view); + } else if (desc.initDataCallback) { + int w = desc.width; + int h = desc.height; + int d = desc.depth; + for (int i = 0; i < (int)desc.initData.size(); i++) { + D3D11_MAPPED_SUBRESOURCE mapped; + hr = context_->Map(tex->stagingTex, i, D3D11_MAP_WRITE, 0, &mapped); + if (!SUCCEEDED(hr)) { + if (i == 0) { + delete tex; + return nullptr; + } else { + break; + } + } + + desc.initDataCallback((uint8_t *)mapped.pData, desc.initData[i], w, h, d, mapped.RowPitch, mapped.DepthPitch); + context_->Unmap(tex->stagingTex, i); + + w = (w + 1) / 2; + h = (h + 1) / 2; + d = (d + 1) / 2; + } + + context_->CopyResource(tex->tex, tex->stagingTex); + tex->stagingTex->Release(); + tex->stagingTex = nullptr; } return tex; } diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 1cec0ae38a..2e5f67b91d 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -133,6 +133,7 @@ static const int primCountDivisor[] = { D3DFORMAT FormatToD3DFMT(DataFormat fmt) { switch (fmt) { case DataFormat::R8G8B8A8_UNORM: return D3DFMT_A8R8G8B8; + case DataFormat::B8G8R8A8_UNORM: return D3DFMT_A8R8G8B8; case DataFormat::R4G4B4A4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // emulated case DataFormat::B4G4R4A4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // native case DataFormat::A4R4G4B4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // emulated @@ -312,7 +313,7 @@ public: void SetToSampler(LPDIRECT3DDEVICE9 device, int sampler); private: - void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); + void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback); bool Create(const TextureDesc &desc); LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9EX deviceEx_; @@ -384,8 +385,14 @@ bool D3D9Texture::Create(const TextureDesc &desc) { // In D3D9, after setting D3DUSAGE_AUTOGENMIPS, we can only access the top layer. The rest will be // automatically generated. int maxLevel = desc.generateMips ? 1 : (int)desc.initData.size(); + int w = desc.width; + int h = desc.height; + int d = desc.depth; for (int i = 0; i < maxLevel; i++) { - SetImageData(0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); + SetImageData(0, 0, 0, w, h, d, i, 0, desc.initData[i], desc.initDataCallback); + w = (w + 1) / 2; + h = (h + 1) / 2; + d = (d + 1) / 2; } } return true; @@ -396,11 +403,10 @@ inline uint32_t Shuffle8888(uint32_t x) { return (x & 0xFF00FF00) | ((x >> 16) & 0xFF) | ((x << 16) & 0xFF0000); } -void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { +void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback) { if (!tex_) return; - if (level == 0) { width_ = width; height_ = height; @@ -417,6 +423,14 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d D3DLOCKED_RECT rect; if (x == 0 && y == 0) { tex_->LockRect(level, &rect, NULL, D3DLOCK_DISCARD); + + if (callback) { + callback((uint8_t *)rect.pBits, data, width, height, depth, rect.Pitch, height * rect.Pitch); + // Now this is the source. All conversions below support in-place. + data = (const uint8_t *)rect.pBits; + stride = rect.Pitch; + } + for (int i = 0; i < height; i++) { uint8_t *dest = (uint8_t *)rect.pBits + rect.Pitch * i; const uint8_t *source = data + stride * i; @@ -431,7 +445,8 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d case DataFormat::A4R4G4B4_UNORM_PACK16: case DataFormat::A1R5G5B5_UNORM_PACK16: // Native - memcpy(dest, source, width * sizeof(uint16_t)); + if (data != rect.pBits) + memcpy(dest, source, width * sizeof(uint16_t)); break; case DataFormat::R8G8B8A8_UNORM: @@ -441,7 +456,8 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d break; case DataFormat::B8G8R8A8_UNORM: - memcpy(dest, source, sizeof(uint32_t) * width); + if (data != rect.pBits) + memcpy(dest, source, sizeof(uint32_t) * width); break; default: // Unhandled data format copy. diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 29acaf614f..e63f692d26 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -655,7 +655,7 @@ public: } private: - void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); + void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback); GLRenderManager *render_; GLRTexture *tex_; @@ -680,14 +680,15 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : canWrap_ = isPowerOf2(width_) && isPowerOf2(height_); mipLevels_ = desc.mipLevels; - if (!desc.initData.size()) + if (desc.initData.empty()) return; int level = 0; for (auto data : desc.initData) { - SetImageData(0, 0, 0, width_, height_, depth_, level, 0, data); + SetImageData(0, 0, 0, width_, height_, depth_, level, 0, data, desc.initDataCallback); width_ = (width_ + 1) / 2; height_ = (height_ + 1) / 2; + depth_ = (depth_ + 1) / 2; level++; } mipLevels_ = desc.generateMips ? desc.mipLevels : level; @@ -699,7 +700,6 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : generatedMips_ = true; } render->FinalizeTexture(tex_, mipLevels_, genMips); - } OpenGLTexture::~OpenGLTexture() { @@ -732,8 +732,8 @@ void MoveABit(u16 *dest, const u16 *src, size_t count) { } } -void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) { - if (width != width_ || height != height_ || depth != depth_) { +void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback) { + if ((width != width_ || height != height_ || depth != depth_) && level == 0) { // When switching to texStorage we need to handle this correctly. width_ = width; height_ = height; @@ -745,19 +745,28 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int size_t alignment = DataFormatSizeInBytes(format_); // Make a copy of data with stride eliminated. - uint8_t *texData = new uint8_t[(size_t)(width * height * alignment)]; + uint8_t *texData = new uint8_t[(size_t)(width * height * depth * alignment)]; - // Emulate support for DataFormat::A1R5G5B5_UNORM_PACK16. - if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) { - format_ = DataFormat::R5G5B5A1_UNORM_PACK16; - for (int y = 0; y < height; y++) { - MoveABit((u16 *)(texData + y * width * alignment), (const u16 *)(data + y * stride * alignment), width); + if (callback) { + callback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment); + if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) { + format_ = DataFormat::R5G5B5A1_UNORM_PACK16; + MoveABit((u16 *)texData, (const u16 *)texData, width * height * depth); } } else { - for (int y = 0; y < height; y++) { - memcpy(texData + y * width * alignment, data + y * stride * alignment, width * alignment); + // Emulate support for DataFormat::A1R5G5B5_UNORM_PACK16. + if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) { + format_ = DataFormat::R5G5B5A1_UNORM_PACK16; + for (int y = 0; y < height; y++) { + MoveABit((u16 *)(texData + y * width * alignment), (const u16 *)(data + y * stride * alignment), width); + } + } else { + for (int y = 0; y < height; y++) { + memcpy(texData + y * width * alignment, data + y * stride * alignment, width * alignment); + } } } + render_->TextureImage(tex_, level, width, height, format_, texData); } diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 33063d7072..13e4f76a29 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -711,15 +711,22 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur if (desc.initData.size()) { int w = width_; int h = height_; + int d = depth_; int i; for (i = 0; i < (int)desc.initData.size(); i++) { uint32_t offset; VkBuffer buf; - size_t size = w * h * bytesPerPixel; - offset = push->PushAligned((const void *)desc.initData[i], size, 16, &buf); + size_t size = w * h * d * bytesPerPixel; + if (desc.initDataCallback) { + uint8_t *dest = (uint8_t *)push->PushAligned(size, &offset, &buf, 16); + desc.initDataCallback(dest, desc.initData[0], w, h, d, w * bytesPerPixel, h * w * bytesPerPixel); + } else { + offset = push->PushAligned((const void *)desc.initData[i], size, 16, &buf); + } vkTex_->UploadMip(cmd, i, w, h, buf, offset, w); w = (w + 1) / 2; h = (h + 1) / 2; + d = (d + 1) / 2; } // Generate the rest of the mips automatically. for (; i < mipLevels_; i++) {