mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
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.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -22,9 +22,11 @@ public:
|
||||
// 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 @@ private:
|
||||
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_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 @@ protected:
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user