Skip to content

Commit

Permalink
Initial implementation of 3D texturing through equal-size mips (see #…
Browse files Browse the repository at this point in the history
…6357)

Vulkan-only currently, though all the other backends except ES 2.0
without GL_OES_texture_3d can support it with some work.
  • Loading branch information
hrydgard committed Jul 30, 2022
1 parent 3e39f62 commit 548c1e0
Show file tree
Hide file tree
Showing 14 changed files with 122 additions and 37 deletions.
12 changes: 7 additions & 5 deletions Common/GPU/Vulkan/VulkanImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
15 changes: 9 additions & 6 deletions Common/GPU/Vulkan/VulkanImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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_;
};
8 changes: 4 additions & 4 deletions Common/GPU/Vulkan/thin3d_vulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down
20 changes: 15 additions & 5 deletions GPU/Common/FragmentShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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");
Expand Down
3 changes: 2 additions & 1 deletion GPU/Common/ShaderId.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ";
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions GPU/Common/ShaderId.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions GPU/Common/ShaderUniforms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions GPU/Common/ShaderUniforms.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down
44 changes: 41 additions & 3 deletions GPU/Common/TextureCacheCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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_) {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1749,6 +1764,7 @@ void TextureCacheCommon::ApplyTexture() {
ApplyTextureFramebuffer(nextFramebufferTexture_, gstate.getTextureFormat(), depth ? NOTIFY_FB_DEPTH : NOTIFY_FB_COLOR);
nextFramebufferTexture_ = nullptr;
}
gstate_c.SetTextureIs3D(false);
return;
}

Expand Down Expand Up @@ -1803,6 +1819,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) {
Expand Down Expand Up @@ -2051,6 +2068,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?
Expand All @@ -2060,7 +2078,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) {
Expand All @@ -2075,7 +2109,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;
Expand Down Expand Up @@ -2134,6 +2168,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;
Expand Down
7 changes: 6 additions & 1 deletion GPU/Common/TextureCacheCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ struct TexCacheEntry {
STATUS_FRAMEBUFFER_OVERLAP = 0x1000,

STATUS_FORCE_REBUILD = 0x2000,

STATUS_3D = 0x4000,
};

// Status, but int so we can zero initialize.
Expand Down Expand Up @@ -263,6 +265,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;
};
Expand Down Expand Up @@ -326,7 +331,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);

template <typename T>
inline const T *GetCurrentClut() {
Expand Down
6 changes: 6 additions & 0 deletions GPU/GPUCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Loading

0 comments on commit 548c1e0

Please sign in to comment.