From f061eadc046b8806a8158b644de2c73742d4f3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 25 Jul 2022 18:51:08 +0200 Subject: [PATCH] Initial implementation of 3D texturing through equal-size mips (see #6357) Vulkan-only currently, though all the other backends except ES 2.0 without GL_OES_texture_3d can support it with some work. --- Common/GPU/Vulkan/VulkanImage.cpp | 12 ++++--- Common/GPU/Vulkan/VulkanImage.h | 15 +++++---- Common/GPU/Vulkan/thin3d_vulkan.cpp | 8 ++--- GPU/Common/FragmentShaderGenerator.cpp | 20 +++++++++--- GPU/Common/ShaderId.cpp | 3 +- GPU/Common/ShaderId.h | 1 + GPU/Common/ShaderUniforms.cpp | 2 ++ GPU/Common/ShaderUniforms.h | 4 +-- GPU/Common/TextureCacheCommon.cpp | 44 ++++++++++++++++++++++++-- GPU/Common/TextureCacheCommon.h | 7 +++- GPU/GPUCommon.cpp | 6 ++++ GPU/GPUState.h | 7 ++++ GPU/Vulkan/DepalettizeShaderVulkan.cpp | 4 +-- GPU/Vulkan/TextureCacheVulkan.cpp | 26 ++++++++++----- 14 files changed, 122 insertions(+), 37 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanImage.cpp b/Common/GPU/Vulkan/VulkanImage.cpp index 8df9696705a2..c013f5d9ee61 100644 --- a/Common/GPU/Vulkan/VulkanImage.cpp +++ b/Common/GPU/Vulkan/VulkanImage.cpp @@ -31,7 +31,7 @@ static bool IsDepthStencilFormat(VkFormat format) { } } -bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { +bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, const VkComponentMapping *mapping) { if (w == 0 || h == 0 || numMips == 0) { ERROR_LOG(G3D, "Can't create a zero-size VulkanTexture"); return false; @@ -41,17 +41,18 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, width_ = w; height_ = h; + depth_ = depth; numMips_ = numMips; format_ = format; VkImageAspectFlags aspect = IsDepthStencilFormat(format) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; VkImageCreateInfo image_create_info{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.imageType = depth > 1 ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; image_create_info.format = format_; image_create_info.extent.width = width_; image_create_info.extent.height = height_; - image_create_info.extent.depth = 1; + image_create_info.extent.depth = depth; image_create_info.mipLevels = numMips; image_create_info.arrayLayers = 1; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; @@ -98,7 +99,7 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, // Create the view while we're at it. VkImageViewCreateInfo view_info{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; view_info.image = image_; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.viewType = depth > 1 ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D; view_info.format = format_; if (mapping) { view_info.components = *mapping; @@ -122,11 +123,12 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, } // TODO: Batch these. -void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength) { +void VulkanTexture::UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, int depthLayer, VkBuffer buffer, uint32_t offset, size_t rowLength) { VkBufferImageCopy copy_region{}; copy_region.bufferOffset = offset; copy_region.bufferRowLength = (uint32_t)rowLength; copy_region.bufferImageHeight = 0; // 2D + copy_region.imageOffset.z = depthLayer; copy_region.imageExtent.width = mipWidth; copy_region.imageExtent.height = mipHeight; copy_region.imageExtent.depth = 1; diff --git a/Common/GPU/Vulkan/VulkanImage.h b/Common/GPU/Vulkan/VulkanImage.h index 75f09a89ae1e..50d7e0c7f586 100644 --- a/Common/GPU/Vulkan/VulkanImage.h +++ b/Common/GPU/Vulkan/VulkanImage.h @@ -22,9 +22,11 @@ class VulkanTexture { // Fast uploads from buffer. Mipmaps supported. // Usage must at least include VK_IMAGE_USAGE_TRANSFER_DST_BIT in order to use UploadMip. // When using UploadMip, initialLayout should be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. - bool CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); + bool CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr); void ClearMip(VkCommandBuffer cmd, int mip, uint32_t value); - void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels + + // Can also be used to copy individual levels of a 3D texture. + void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, int depthLayer, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels void GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bool fromCompute); void EndCreate(VkCommandBuffer cmd, bool vertexTexture, VkPipelineStageFlags prevStage, VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); @@ -62,10 +64,11 @@ class VulkanTexture { VkImageView view_ = VK_NULL_HANDLE; VmaAllocation allocation_ = VK_NULL_HANDLE; - int32_t width_ = 0; - int32_t height_ = 0; - int32_t numMips_ = 1; + int16_t width_ = 0; + int16_t height_ = 0; + int16_t numMips_ = 1; + int16_t depth_ = 1; + VkFormat format_ = VK_FORMAT_UNDEFINED; - size_t offset_ = 0; std::string tag_; }; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index d4276192cfe1..1529f4626bcf 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -640,7 +640,7 @@ VulkanTexture *VKContext::GetNullTexture() { nullTexture_->SetTag("Null"); int w = 8; int h = 8; - nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + nullTexture_->CreateDirect(cmdInit, w, h, 1, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); uint32_t bindOffset; VkBuffer bindBuf; @@ -651,7 +651,7 @@ VulkanTexture *VKContext::GetNullTexture() { data[y*w + x] = 0; // black } } - nullTexture_->UploadMip(cmdInit, 0, w, h, bindBuf, bindOffset, w); + nullTexture_->UploadMip(cmdInit, 0, w, h, 0, bindBuf, bindOffset, w); nullTexture_->EndCreate(cmdInit, false, VK_PIPELINE_STAGE_TRANSFER_BIT); } else { nullTexture_->Touch(); @@ -733,7 +733,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur usageBits |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } - if (!vkTex_->CreateDirect(cmd, width_, height_, mipLevels_, vulkanFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, usageBits)) { + if (!vkTex_->CreateDirect(cmd, width_, height_, 1, mipLevels_, vulkanFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, usageBits)) { ERROR_LOG(G3D, "Failed to create VulkanTexture: %dx%dx%d fmt %d, %d levels", width_, height_, depth_, (int)vulkanFormat, mipLevels_); return false; } @@ -755,7 +755,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur } else { offset = push->PushAligned((const void *)desc.initData[i], size, 16, &buf); } - vkTex_->UploadMip(cmd, i, w, h, buf, offset, w); + vkTex_->UploadMip(cmd, i, w, h, 0, buf, offset, w); w = (w + 1) / 2; h = (h + 1) / 2; d = (d + 1) / 2; diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index df12e041d4d0..285d81353181 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -78,6 +78,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu bool enableColorDoubling = id.Bit(FS_BIT_COLOR_DOUBLE); bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ); bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA); + bool texture3D = id.Bit(FS_BIT_3D_TEXTURE); bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled; @@ -136,7 +137,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseUBO {\n%s};\n", ub_baseStr); if (doTexture) { - WRITE(p, "layout (binding = 0) uniform sampler2D tex;\n"); + WRITE(p, "layout (binding = 0) uniform %s tex;\n", texture3D ? "sampler3D" : "sampler2D"); } if (readFramebufferTex) { @@ -558,10 +559,19 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " vec4 t = tex2D(tex, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : ""); } } else { - if (doTextureProjection) { - WRITE(p, " vec4 t = %sProj(tex, %s);\n", compat.texture, texcoord); + if (texture3D) { + WRITE(p, " float bias = pow(u_mipBias, 1.0);\n"); + if (doTextureProjection) { + WRITE(p, " vec4 t = %sProj(tex, vec4(%s.xy, bias, %s.z));\n", compat.texture, texcoord, texcoord); + } else { + WRITE(p, " vec4 t = %s(tex, vec3(%s.xy, bias));\n", compat.texture, texcoord); + } } else { - WRITE(p, " vec4 t = %s(tex, %s.xy);\n", compat.texture, texcoord); + if (doTextureProjection) { + WRITE(p, " vec4 t = %sProj(tex, %s);\n", compat.texture, texcoord); + } else { + WRITE(p, " vec4 t = %s(tex, %s.xy);\n", compat.texture, texcoord); + } } } } else { @@ -572,7 +582,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } else { WRITE(p, " vec2 uv = %s.xy;\n vec2 uv_round;\n", texcoord); } - WRITE(p, " vec2 tsize = vec2(textureSize(tex, 0));\n"); + WRITE(p, " vec2 tsize = textureSize(tex, 0).xy;\n"); WRITE(p, " vec2 fraction;\n"); WRITE(p, " bool bilinear = (u_depal_mask_shift_off_fmt >> 31) != 0U;\n"); WRITE(p, " if (bilinear) {\n"); diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 2a7441a92fa9..12a5996b5d11 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -170,7 +170,7 @@ std::string FragmentShaderDesc(const FShaderID &id) { std::stringstream desc; desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]); if (id.Bit(FS_BIT_CLEARMODE)) desc << "Clear "; - if (id.Bit(FS_BIT_DO_TEXTURE)) desc << "Tex "; + if (id.Bit(FS_BIT_DO_TEXTURE)) desc << (id.Bit(FS_BIT_3D_TEXTURE) ? "Tex3D " : "Tex "); if (id.Bit(FS_BIT_DO_TEXTURE_PROJ)) desc << "TexProj "; if (id.Bit(FS_BIT_TEXALPHA)) desc << "TexAlpha "; if (id.Bit(FS_BIT_TEXTURE_AT_OFFSET)) desc << "TexOffs "; @@ -289,6 +289,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const Draw::Bugs &bugs) { } id.SetBit(FS_BIT_BGRA_TEXTURE, gstate_c.bgraTexture); id.SetBit(FS_BIT_SHADER_DEPAL, useShaderDepal); + id.SetBit(FS_BIT_3D_TEXTURE, gstate_c.curTextureIs3D); } id.SetBit(FS_BIT_LMODE, lmode); diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 3c1d75226f94..8dcee32c1ea4 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -93,6 +93,7 @@ enum FShaderBit : uint8_t { FS_BIT_TEST_DISCARD_TO_ZERO = 48, FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL = 49, FS_BIT_COLOR_WRITEMASK = 50, + FS_BIT_3D_TEXTURE = 51, }; static inline FShaderBit operator +(FShaderBit bit, int i) { diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index b07d4d1fdc6f..0fe8ce807ad3 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -104,6 +104,8 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView ub->texClamp[3] = invH * 0.5f; ub->texClampOffset[0] = gstate_c.curTextureXOffset * invW; ub->texClampOffset[1] = gstate_c.curTextureYOffset * invH; + + ub->mipBias = (float)gstate.getTexLevelOffset16() * (1.0 / 16.0f); } if (dirtyUniforms & DIRTY_PROJMATRIX) { diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index 1195af454745..691835a2628d 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -34,7 +34,7 @@ struct UB_VS_FS_Base { float cullRangeMin[4]; float cullRangeMax[4]; uint32_t spline_counts; uint32_t depal_mask_shift_off_fmt; // 4 params packed into one. - uint32_t colorWriteMask; int pad3; + uint32_t colorWriteMask; float mipBias; // Fragment data float fogColor[4]; float texEnvColor[4]; @@ -63,7 +63,7 @@ R"( mat4 u_proj; uint u_spline_counts; uint u_depal_mask_shift_off_fmt; uint u_colorWriteMask; - int u_pad3; + float u_mipBias; vec3 u_fogcolor; vec3 u_texenv; ivec4 u_alphacolorref; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 74a63fcf97cc..bd214bbb4bf5 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -481,7 +481,8 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { if (match && (entry->status & TexCacheEntry::STATUS_TO_REPLACE) && replacementTimeThisFrame_ < replacementFrameBudget_) { int w0 = gstate.getTextureWidth(0); int h0 = gstate.getTextureHeight(0); - ReplacedTexture &replaced = FindReplacement(entry, w0, h0); + int d0 = 1; + ReplacedTexture &replaced = FindReplacement(entry, w0, h0, d0); if (replaced.Valid()) { match = false; reason = "replacing"; @@ -492,6 +493,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { // got one! gstate_c.curTextureWidth = w; gstate_c.curTextureHeight = h; + gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); if (rehash) { // Update in case any of these changed. entry->sizeInRAM = (textureBitsPerPixel[format] * bufw * h / 2) / 8; @@ -597,6 +599,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { gstate_c.curTextureWidth = w; gstate_c.curTextureHeight = h; + gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); nextTexture_ = entry; if (nextFramebufferTexture_) { @@ -1132,6 +1135,13 @@ void TextureCacheCommon::NotifyVideoUpload(u32 addr, int size, int width, GEBuff } void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { + if (loadBytes == 0) { + // Don't accidentally overwrite clutTotalBytes_ with a zero. + return; + } + + u32 startPos = gstate.getClutIndexStartPos(); + clutTotalBytes_ = loadBytes; clutRenderAddress_ = 0xFFFFFFFF; @@ -1288,7 +1298,12 @@ u32 TextureCacheCommon::EstimateTexMemoryUsage(const TexCacheEntry *entry) { return pixelSize << (dimW + dimH); } -ReplacedTexture &TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &w, int &h) { +ReplacedTexture &TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d) { + if (d != 1) { + // We don't yet support replacing 3D textures. + return replacer_.FindNone(); + } + // Short circuit the non-enabled case. // Otherwise, due to bReplaceTexturesAllowLate, we'll still spawn tasks looking for replacements // that then won't be used. @@ -1751,6 +1766,7 @@ void TextureCacheCommon::ApplyTexture() { ApplyTextureFramebuffer(nextFramebufferTexture_, gstate.getTextureFormat(), depth ? NOTIFY_FB_DEPTH : NOTIFY_FB_COLOR); nextFramebufferTexture_ = nullptr; } + gstate_c.SetTextureIs3D(false); return; } @@ -1805,6 +1821,7 @@ void TextureCacheCommon::ApplyTexture() { entry->lastFrame = gpuStats.numFlips; BindTexture(entry); gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); + gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); } void TextureCacheCommon::Clear(bool delete_them) { @@ -2055,6 +2072,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } plan.scaleFactor = standardScaleFactor_; + plan.depth = 1; // Rachet down scale factor in low-memory mode. // TODO: I think really we should just turn it off? @@ -2064,7 +2082,23 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } if (plan.badMipSizes) { + // Check for pure 3D texture. + int tw = gstate.getTextureWidth(0); + int th = gstate.getTextureHeight(0); + bool pure3D = true; + for (int i = 0; i < plan.levelsToLoad; i++) { + if (gstate.getTextureWidth(i) != gstate.getTextureWidth(0) || gstate.getTextureHeight(i) != gstate.getTextureHeight(0)) { + pure3D = false; + } + } + + if (pure3D) { + plan.depth = plan.levelsToLoad; + plan.scaleFactor = 1; + } + plan.levelsToLoad = 1; + plan.levelsToCreate = 1; } if (plan.hardwareScaling) { @@ -2079,7 +2113,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.w = gstate.getTextureWidth(0); plan.h = gstate.getTextureHeight(0); - plan.replaced = &FindReplacement(entry, plan.w, plan.h); + plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth); if (plan.replaced->Valid()) { // We're replacing, so we won't scale. plan.scaleFactor = 1; @@ -2138,6 +2172,10 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt entry->status &= ~TexCacheEntry::STATUS_NO_MIPS; } + if (plan.depth > 1) { + entry->status |= TexCacheEntry::STATUS_3D; + } + // Will be filled in again during decode. entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK; return true; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 33430d6c9762..5c8630bb81a4 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -135,6 +135,8 @@ struct TexCacheEntry { STATUS_FRAMEBUFFER_OVERLAP = 0x1000, STATUS_FORCE_REBUILD = 0x2000, + + STATUS_3D = 0x4000, }; // Status, but int so we can zero initialize. @@ -264,6 +266,9 @@ struct BuildTexturePlan { int w; int h; + // Used for 3D textures only. If not a 3D texture, will be 1. + int depth; + // The replacement for the texture. ReplacedTexture *replaced; }; @@ -327,7 +332,7 @@ class TextureCacheCommon { CheckAlphaResult DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool expandTo32Bit); void UnswizzleFromMem(u32 *dest, u32 destPitch, const u8 *texptr, u32 bufw, u32 height, u32 bytesPerPixel); CheckAlphaResult ReadIndexedTex(u8 *out, int outPitch, int level, const u8 *texptr, int bytesPerIndex, int bufw, bool reverseColors, bool expandTo32Bit); - ReplacedTexture &FindReplacement(TexCacheEntry *entry, int &w, int &h); + ReplacedTexture &FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d); // Return value is mapData normally, but could be another buffer allocated with AllocateAlignedMemory. void LoadTextureLevel(TexCacheEntry &entry, uint8_t *mapData, int mapRowPitch, ReplacedTexture &replaced, int srcLevel, int scaleFactor, Draw::DataFormat dstFmt, bool reverseColors); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index d47eb0688120..eb5bf312e45a 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1565,6 +1565,12 @@ void GPUCommon::Execute_TexLevel(u32 op, u32 diff) { Flush(); } gstate.texlevel ^= diff; + + if (diff & 0xFF0000) { + // Piggyback on this flag for 3D textures. + gstate_c.Dirty(DIRTY_TEXCLAMP); + } + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE); } diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 9026cfc24f18..4bd4dae8902b 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -551,6 +551,12 @@ struct GPUStateCache { Dirty(DIRTY_FRAGMENTSHADER_STATE); } } + void SetTextureIs3D(bool is3D) { + if (is3D != curTextureIs3D) { + curTextureIs3D = is3D; + Dirty(DIRTY_FRAGMENTSHADER_STATE | DIRTY_UVSCALEOFFSET); + } + } u32 featureFlags; @@ -580,6 +586,7 @@ struct GPUStateCache { // Only applied when needShaderTexClamp = true. u32 curTextureXOffset; u32 curTextureYOffset; + bool curTextureIs3D; float vpWidth; float vpHeight; diff --git a/GPU/Vulkan/DepalettizeShaderVulkan.cpp b/GPU/Vulkan/DepalettizeShaderVulkan.cpp index a72c2af03815..52269387bf7f 100644 --- a/GPU/Vulkan/DepalettizeShaderVulkan.cpp +++ b/GPU/Vulkan/DepalettizeShaderVulkan.cpp @@ -168,7 +168,7 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat VulkanTexture *vktex = new VulkanTexture(vulkan); vktex->SetTag("DepalClut"); VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); - if (!vktex->CreateDirect(cmd, texturePixels, 1, 1, destFormat, + if (!vktex->CreateDirect(cmd, texturePixels, 1, 1, 1, destFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, &componentMapping)) { ERROR_LOG(G3D, "Failed to create texture for CLUT"); return nullptr; @@ -177,7 +177,7 @@ VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat VkBuffer pushBuffer; uint32_t pushOffset = push_->PushAligned(rawClut, 1024, 4, &pushBuffer); - vktex->UploadMip(cmd, 0, texturePixels, 1, pushBuffer, pushOffset, texturePixels); + vktex->UploadMip(cmd, 0, texturePixels, 1, 0, pushBuffer, pushOffset, texturePixels); vktex->EndCreate(cmd, false, VK_PIPELINE_STAGE_TRANSFER_BIT); DepalTextureVulkan *tex = new DepalTextureVulkan(); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index f396c57769b7..1b52ec568964 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -652,7 +652,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); image->SetTag(texName); - bool allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); + bool allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); if (!allocSuccess && !lowMemoryMode_) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; @@ -671,7 +671,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { plan.scaleFactor = 1; actualFmt = dstFmt; - allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); + allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping); } if (!allocSuccess) { @@ -686,7 +686,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { ReplacedTextureDecodeInfo replacedInfo; bool willSaveTex = false; - if (replacer_.Enabled() && !plan.replaced->Valid()) { + if (replacer_.Enabled() && !plan.replaced->Valid() && plan.depth == 1) { replacedInfo.cachekey = entry->CacheKey(); replacedInfo.hash = entry->fullhash; replacedInfo.addr = entry->addr; @@ -700,8 +700,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "Texture Upload (%08x) video=%d", entry->addr, plan.isVideo); - // Upload the texture data. - for (int i = 0; i < plan.levelsToLoad; i++) { + // Upload the texture data. We simply reuse the same loop for 3D texture slices instead of mips, if we have those. + int levels; + if (plan.depth > 1) { + levels = plan.depth; + } else { + levels = plan.levelsToLoad; + } + + for (int i = 0; i < levels; i++) { int mipUnscaledWidth = gstate.getTextureWidth(i); int mipUnscaledHeight = gstate.getTextureHeight(i); @@ -742,10 +749,13 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { replacementTimeThisFrame_ += time_now_d() - replaceStart; VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT, "Copy Upload (replaced): %dx%d", mipWidth, mipHeight); - entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, stride / bpp); VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT); } else { - if (computeUpload) { + if (plan.depth != 1) { + loadLevel(size, i, stride, plan.scaleFactor); + entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, stride / bpp); + } else if (computeUpload) { int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2; int srcStride = mipUnscaledWidth * srcBpp; int srcSize = srcStride * mipUnscaledHeight; @@ -768,7 +778,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { loadLevel(size, i == 0 ? plan.baseLevelSrc : i, stride, plan.scaleFactor); VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT, "Copy Upload: %dx%d", mipWidth, mipHeight); - entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, stride / bpp); VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT); } if (replacer_.Enabled()) {