Merge pull request #15727 from hrydgard/texture-3d

Implement the PSP's equal-size mips "3D texturing"
This commit is contained in:
Henrik Rydgård 2022-08-01 08:35:43 +02:00 committed by GitHub
commit e1956b47a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 450 additions and 192 deletions

View file

@ -283,6 +283,7 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
caps_.framebufferCopySupported = true;
caps_.framebufferDepthBlitSupported = false;
caps_.framebufferDepthCopySupported = true;
caps_.texture3DSupported = true;
D3D11_FEATURE_DATA_D3D11_OPTIONS options{};
HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options));

View file

@ -59,6 +59,7 @@ void DirectXState::Restore() {
texMaxMipLevel.restore(); count++;
texAddressU.restore(); count++;
texAddressV.restore(); count++;
texAddressW.restore(); count++;
}
} // namespace DX9

View file

@ -393,6 +393,7 @@ public:
DxSampler0State1<D3DSAMP_MAXMIPLEVEL, 0> texMaxMipLevel;
DxSampler0State1<D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP> texAddressU;
DxSampler0State1<D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP> texAddressV;
DxSampler0State1<D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP> texAddressW;
};
#undef STATE1

View file

@ -667,6 +667,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
caps_.framebufferCopySupported = false;
caps_.framebufferDepthBlitSupported = true;
caps_.framebufferDepthCopySupported = false;
caps_.texture3DSupported = true;
if (d3d) {
D3DDISPLAYMODE displayMode;
d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode);

View file

@ -383,6 +383,7 @@ void CheckGLExtensions() {
gl_extensions.EXT_shader_framebuffer_fetch = g_set_gl_extensions.count("GL_EXT_shader_framebuffer_fetch") != 0;
gl_extensions.ARM_shader_framebuffer_fetch = g_set_gl_extensions.count("GL_ARM_shader_framebuffer_fetch") != 0;
gl_extensions.OES_texture_float = g_set_gl_extensions.count("GL_OES_texture_float") != 0;
gl_extensions.OES_texture_3D = g_set_gl_extensions.count("GL_OES_texture_3D") != 0;
gl_extensions.EXT_buffer_storage = g_set_gl_extensions.count("GL_EXT_buffer_storage") != 0;
gl_extensions.EXT_clip_cull_distance = g_set_gl_extensions.count("GL_EXT_clip_cull_distance") != 0;
gl_extensions.APPLE_clip_distance = g_set_gl_extensions.count("GL_APPLE_clip_distance") != 0;

View file

@ -51,6 +51,7 @@ struct GLExtensions {
bool OES_vertex_array_object;
bool OES_copy_image;
bool OES_texture_float;
bool OES_texture_3D;
// ARB
bool ARB_framebuffer_object;

View file

@ -353,7 +353,17 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
GLenum internalFormat, format, type;
int alignment;
Thin3DFormatToFormatAndType(step.texture_image.format, internalFormat, format, type, alignment);
glTexImage2D(tex->target, step.texture_image.level, internalFormat, step.texture_image.width, step.texture_image.height, 0, format, type, step.texture_image.data);
if (step.texture_image.depth == 1) {
glTexImage2D(tex->target,
step.texture_image.level, internalFormat,
step.texture_image.width, step.texture_image.height, 0,
format, type, step.texture_image.data);
} else {
glTexImage3D(tex->target,
step.texture_image.level, internalFormat,
step.texture_image.width, step.texture_image.height, step.texture_image.depth, 0,
format, type, step.texture_image.data);
}
allocatedTextures = true;
if (step.texture_image.allocType == GLRAllocType::ALIGNED) {
FreeAlignedMemory(step.texture_image.data);
@ -369,6 +379,9 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, tex->wrapT);
glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, tex->magFilter);
glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, tex->minFilter);
if (step.texture_image.depth > 1) {
glTexParameteri(tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
CHECK_GL_ERROR_IF_DEBUG();
break;
}
@ -380,7 +393,7 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
glBindTexture(tex->target, tex->texture);
boundTexture = tex->texture;
}
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
if ((!gl_extensions.IsGLES || gl_extensions.GLES3) && step.texture_finalize.loadedLevels > 1) {
glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.loadedLevels - 1);
}
tex->maxLod = (float)step.texture_finalize.loadedLevels - 1;
@ -1144,28 +1157,28 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
CHECK_GL_ERROR_IF_DEBUG();
if (tex->canWrap) {
if (tex->wrapS != c.textureSampler.wrapS) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS);
glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS);
tex->wrapS = c.textureSampler.wrapS;
}
if (tex->wrapT != c.textureSampler.wrapT) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT);
glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT);
tex->wrapT = c.textureSampler.wrapT;
}
}
CHECK_GL_ERROR_IF_DEBUG();
if (tex->magFilter != c.textureSampler.magFilter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter);
glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter);
tex->magFilter = c.textureSampler.magFilter;
}
CHECK_GL_ERROR_IF_DEBUG();
if (tex->minFilter != c.textureSampler.minFilter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter);
glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter);
tex->minFilter = c.textureSampler.minFilter;
}
CHECK_GL_ERROR_IF_DEBUG();
if (tex->anisotropy != c.textureSampler.anisotropy) {
if (c.textureSampler.anisotropy != 0.0f) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy);
glTexParameterf(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy);
}
tex->anisotropy = c.textureSampler.anisotropy;
}
@ -1185,16 +1198,16 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
}
#ifndef USING_GLES2
if (tex->lodBias != c.textureLod.lodBias && !gl_extensions.IsGLES) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias);
glTexParameterf(tex->target, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias);
tex->lodBias = c.textureLod.lodBias;
}
#endif
if (tex->minLod != c.textureLod.minLod) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, c.textureLod.minLod);
glTexParameterf(tex->target, GL_TEXTURE_MIN_LOD, c.textureLod.minLod);
tex->minLod = c.textureLod.minLod;
}
if (tex->maxLod != c.textureLod.maxLod) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod);
glTexParameterf(tex->target, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod);
tex->maxLod = c.textureLod.maxLod;
}
break;
@ -1205,6 +1218,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
// TODO: Need bind?
if (!c.texture_subimage.data)
Crash();
_assert_(tex->target == GL_TEXTURE_2D);
// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.
GLuint internalFormat, format, type;
int alignment;

View file

@ -259,8 +259,9 @@ struct GLRInitStep {
GLRTexture *texture;
Draw::DataFormat format;
int level;
int width;
int height;
uint16_t width;
uint16_t height;
uint16_t depth;
GLRAllocType allocType;
bool linearFilter;
uint8_t *data; // owned, delete[]-d

View file

@ -26,7 +26,7 @@ static bool OnRenderThread() {
}
#endif
GLRTexture::GLRTexture(int width, int height, int numMips) {
GLRTexture::GLRTexture(int width, int height, int depth, int numMips) {
if (gl_extensions.OES_texture_npot) {
canWrap = true;
} else {
@ -34,6 +34,7 @@ GLRTexture::GLRTexture(int width, int height, int numMips) {
}
w = width;
h = height;
depth = depth;
this->numMips = numMips;
}

View file

@ -25,12 +25,13 @@ constexpr int MAX_GL_TEXTURE_SLOTS = 8;
class GLRTexture {
public:
GLRTexture(int width, int height, int numMips);
GLRTexture(int width, int height, int depth, int numMips);
~GLRTexture();
GLuint texture = 0;
uint16_t w;
uint16_t h;
uint16_t d;
// We don't trust OpenGL defaults - setting wildly off values ensures that we'll end up overwriting these parameters.
GLenum target = 0xFFFF;
@ -49,7 +50,7 @@ public:
class GLRFramebuffer {
public:
GLRFramebuffer(int _width, int _height, bool z_stencil)
: color_texture(_width, _height, 1), z_stencil_texture(_width, _height, 1),
: color_texture(_width, _height, 1, 1), z_stencil_texture(_width, _height, 1, 1),
width(_width), height(_height), z_stencil_(z_stencil) {
}
@ -384,9 +385,9 @@ public:
// Creation commands. These were not needed in Vulkan since there we can do that on the main thread.
// We pass in width/height here even though it's not strictly needed until we support glTextureStorage
// and then we'll also need formats and stuff.
GLRTexture *CreateTexture(GLenum target, int width, int height, int numMips) {
GLRTexture *CreateTexture(GLenum target, int width, int height, int depth, int numMips) {
GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE };
step.create_texture.texture = new GLRTexture(width, height, numMips);
step.create_texture.texture = new GLRTexture(width, height, depth, numMips);
step.create_texture.texture->target = target;
initSteps_.push_back(step);
return step.create_texture.texture;
@ -537,7 +538,7 @@ public:
}
// Takes ownership over the data pointer and delete[]-s it.
void TextureImage(GLRTexture *texture, int level, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW, bool linearFilter = false) {
void TextureImage(GLRTexture *texture, int level, int width, int height, int depth, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW, bool linearFilter = false) {
GLRInitStep step{ GLRInitStepType::TEXTURE_IMAGE };
step.texture_image.texture = texture;
step.texture_image.data = data;
@ -545,6 +546,7 @@ public:
step.texture_image.level = level;
step.texture_image.width = width;
step.texture_image.height = height;
step.texture_image.depth = depth;
step.texture_image.allocType = allocType;
step.texture_image.linearFilter = linearFilter;
initSteps_.push_back(step);

View file

@ -536,8 +536,10 @@ OpenGLContext::OpenGLContext() {
} else {
caps_.preferredDepthBufferFormat = DataFormat::D16;
}
caps_.texture3DSupported = gl_extensions.OES_texture_3D;
} else {
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
caps_.texture3DSupported = true;
}
caps_.framebufferBlitSupported = gl_extensions.NV_framebuffer_blit || gl_extensions.ARB_framebuffer_object;
caps_.framebufferDepthBlitSupported = caps_.framebufferBlitSupported;
@ -636,6 +638,7 @@ OpenGLContext::OpenGLContext() {
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_3xx;
shaderLanguageDesc_.fragColor0 = "fragColor0";
shaderLanguageDesc_.texture = "texture";
shaderLanguageDesc_.texture3D = "texture";
shaderLanguageDesc_.glslES30 = true;
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch";
@ -659,6 +662,7 @@ OpenGLContext::OpenGLContext() {
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_3xx;
shaderLanguageDesc_.fragColor0 = "fragColor0";
shaderLanguageDesc_.texture = "texture";
shaderLanguageDesc_.texture3D = "texture";
shaderLanguageDesc_.glslES30 = true;
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch";
@ -669,6 +673,7 @@ OpenGLContext::OpenGLContext() {
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;
shaderLanguageDesc_.fragColor0 = "fragColor0";
shaderLanguageDesc_.texture = "texture";
shaderLanguageDesc_.texture3D = "texture";
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch";
shaderLanguageDesc_.varying_vs = "out";
@ -792,7 +797,7 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
format_ = desc.format;
type_ = desc.type;
GLenum target = TypeToTarget(desc.type);
tex_ = render->CreateTexture(target, desc.width, desc.height, desc.mipLevels);
tex_ = render->CreateTexture(target, desc.width, desc.height, 1, desc.mipLevels);
mipLevels_ = desc.mipLevels;
if (desc.initData.empty())
@ -877,7 +882,7 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int
}
}
render_->TextureImage(tex_, level, width, height, format_, texData);
render_->TextureImage(tex_, level, width, height, depth, format_, texData);
}
#ifdef DEBUG_READ_PIXELS

View file

@ -44,6 +44,7 @@ void ShaderLanguageDesc::Init(ShaderLanguage lang) {
fragColor0 = "gl_FragColor";
fragColor1 = "fragColor1";
texture = "texture2D";
texture3D = "texture3D";
texelFetch = nullptr;
bitwiseOps = false;
lastFragData = nullptr;
@ -59,6 +60,7 @@ void ShaderLanguageDesc::Init(ShaderLanguage lang) {
fragColor0 = "fragColor0";
fragColor1 = "fragColor1";
texture = "texture";
texture3D = "texture";
texelFetch = "texelFetch";
bitwiseOps = true;
lastFragData = nullptr;
@ -80,6 +82,7 @@ void ShaderLanguageDesc::Init(ShaderLanguage lang) {
glslVersionNumber = 450;
lastFragData = nullptr;
texture = "texture";
texture3D = "texture";
texelFetch = "texelFetch";
forceMatrix4x4 = false;
coefsFromBuffers = true;
@ -102,6 +105,7 @@ void ShaderLanguageDesc::Init(ShaderLanguage lang) {
glslVersionNumber = 0;
lastFragData = nullptr;
texture = "texture";
texture3D = "texture";
texelFetch = "texelFetch";
forceMatrix4x4 = false;
coefsFromBuffers = true;

View file

@ -46,6 +46,7 @@ struct ShaderLanguageDesc {
const char *fragColor0 = nullptr;
const char *fragColor1 = nullptr;
const char *texture = nullptr;
const char *texture3D = nullptr;
const char *texelFetch = nullptr;
const char *lastFragData = nullptr;
const char *framebufferFetchExtension = nullptr;

View file

@ -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;

View file

@ -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_;
};

View file

@ -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;
@ -787,6 +787,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
caps_.framebufferDepthBlitSupported = false; // Can be checked for.
caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case.
caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan.
caps_.texture3DSupported = true;
auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties;
switch (deviceProps.vendorID) {

View file

@ -532,6 +532,8 @@ struct DeviceCaps {
bool framebufferDepthCopySupported;
bool framebufferDepthBlitSupported;
bool framebufferFetchSupported;
bool texture3DSupported;
std::string deviceName; // The device name to use when creating the thin3d context, to get the same one.
};

View file

@ -49,6 +49,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
highpTexcoord = highpFog;
}
bool texture3D = id.Bit(FS_BIT_3D_TEXTURE);
ReplaceAlphaType stencilToAlpha = static_cast<ReplaceAlphaType>(id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2));
std::vector<const char*> gl_exts;
@ -62,6 +64,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
if (compat.framebufferFetchExtension) {
gl_exts.push_back(compat.framebufferFetchExtension);
}
if (gl_extensions.OES_texture_3D && texture3D) {
gl_exts.push_back("#extension GL_OES_texture_3D: enable");
}
}
ShaderWriter p(buffer, compat, ShaderStage::Fragment, gl_exts.data(), gl_exts.size());
@ -82,7 +87,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled;
bool doFlatShading = id.Bit(FS_BIT_FLATSHADE) && !flatBug;
bool shaderDepal = id.Bit(FS_BIT_SHADER_DEPAL);
bool shaderDepal = id.Bit(FS_BIT_SHADER_DEPAL) && !texture3D; // combination with texture3D not supported. Enforced elsewhere too.
bool bgraTexture = id.Bit(FS_BIT_BGRA_TEXTURE);
bool colorWriteMask = id.Bit(FS_BIT_COLOR_WRITEMASK) && compat.bitwiseOps;
@ -136,7 +141,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) {
@ -207,9 +212,16 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
if (enableFog) {
WRITE(p, "float3 u_fogcolor : register(c%i);\n", CONST_PS_FOGCOLOR);
}
if (texture3D) {
WRITE(p, "float u_mipBias : register(c%i);\n", CONST_PS_MIPBIAS);
}
} else {
WRITE(p, "SamplerState samp : register(s0);\n");
WRITE(p, "Texture2D<vec4> tex : register(t0);\n");
if (texture3D) {
WRITE(p, "Texture3D<vec4> tex : register(t0);\n");
} else {
WRITE(p, "Texture2D<vec4> tex : register(t0);\n");
}
if (readFramebufferTex) {
// No sampler required, we Load
WRITE(p, "Texture2D<vec4> fboTex : register(t1);\n");
@ -263,48 +275,19 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
}
WRITE(p, "};\n");
}
} else if (compat.shaderLanguage == HLSL_D3D9) {
if (doTexture)
WRITE(p, "sampler tex : register(s0);\n");
if (readFramebufferTex) {
WRITE(p, "vec2 u_fbotexSize : register(c%i);\n", CONST_PS_FBOTEXSIZE);
WRITE(p, "sampler fbotex : register(s1);\n");
}
if (replaceBlend > REPLACE_BLEND_STANDARD) {
if (replaceBlendFuncA >= GE_SRCBLEND_FIXA) {
WRITE(p, "float3 u_blendFixA : register(c%i);\n", CONST_PS_BLENDFIXA);
}
if (replaceBlendFuncB >= GE_DSTBLEND_FIXB) {
WRITE(p, "float3 u_blendFixB : register(c%i);\n", CONST_PS_BLENDFIXB);
}
}
if (needShaderTexClamp && doTexture) {
WRITE(p, "vec4 u_texclamp : register(c%i);\n", CONST_PS_TEXCLAMP);
if (textureAtOffset) {
WRITE(p, "vec2 u_texclampoff : register(c%i);\n", CONST_PS_TEXCLAMPOFF);
}
}
if (enableAlphaTest || enableColorTest) {
WRITE(p, "vec4 u_alphacolorref : register(c%i);\n", CONST_PS_ALPHACOLORREF);
WRITE(p, "vec4 u_alphacolormask : register(c%i);\n", CONST_PS_ALPHACOLORMASK);
}
if (stencilToAlpha && replaceAlphaWithStencilType == STENCIL_VALUE_UNIFORM) {
WRITE(p, "float u_stencilReplaceValue : register(c%i);\n", CONST_PS_STENCILREPLACE);
}
if (doTexture && texFunc == GE_TEXFUNC_BLEND) {
WRITE(p, "float3 u_texenv : register(c%i);\n", CONST_PS_TEXENV);
}
if (enableFog) {
WRITE(p, "float3 u_fogcolor : register(c%i);\n", CONST_PS_FOGCOLOR);
}
} else if (ShaderLanguageIsOpenGL(compat.shaderLanguage)) {
if ((shaderDepal || colorWriteMask) && gl_extensions.IsGLES) {
WRITE(p, "precision highp int;\n");
}
if (doTexture)
WRITE(p, "uniform sampler2D tex;\n");
if (doTexture) {
if (texture3D) {
// For whatever reason, a precision specifier is required here.
WRITE(p, "uniform lowp sampler3D tex;\n");
} else {
WRITE(p, "uniform sampler2D tex;\n");
}
}
if (readFramebufferTex) {
if (!compat.texelFetch) {
@ -364,6 +347,11 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
WRITE(p, "uniform vec3 u_texenv;\n");
}
if (texture3D) {
*uniformMask |= DIRTY_MIPBIAS;
WRITE(p, "uniform float u_mipBias;\n");
}
WRITE(p, "%s %s lowp vec4 v_color0;\n", shading, compat.varying_fs);
if (lmode)
WRITE(p, "%s %s lowp vec3 v_color1;\n", shading, compat.varying_fs);
@ -546,24 +534,50 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
if (!shaderDepal) {
if (compat.shaderLanguage == HLSL_D3D11) {
if (doTextureProjection) {
WRITE(p, " vec4 t = tex.Sample(samp, v_texcoord.xy / v_texcoord.z)%s;\n", bgraTexture ? ".bgra" : "");
if (texture3D) {
if (doTextureProjection) {
WRITE(p, " vec4 t = tex.Sample(samp, vec3(v_texcoord.xy / v_texcoord.z, u_mipBias))%s;\n", bgraTexture ? ".bgra" : "");
} else {
WRITE(p, " vec4 t = tex.Sample(samp, vec3(%s.xy, u_mipBias))%s;\n", texcoord, bgraTexture ? ".bgra" : "");
}
} else {
WRITE(p, " vec4 t = tex.Sample(samp, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : "");
if (doTextureProjection) {
WRITE(p, " vec4 t = tex.Sample(samp, v_texcoord.xy / v_texcoord.z)%s;\n", bgraTexture ? ".bgra" : "");
} else {
WRITE(p, " vec4 t = tex.Sample(samp, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : "");
}
}
} else if (compat.shaderLanguage == HLSL_D3D9) {
if (doTextureProjection) {
WRITE(p, " vec4 t = tex2Dproj(tex, vec4(v_texcoord.x, v_texcoord.y, 0, v_texcoord.z))%s;\n", bgraTexture ? ".bgra" : "");
if (texture3D) {
if (doTextureProjection) {
WRITE(p, " vec4 t = tex3Dproj(tex, vec4(v_texcoord.x, v_texcoord.y, u_mipBias, v_texcoord.z))%s;\n", bgraTexture ? ".bgra" : "");
} else {
WRITE(p, " vec4 t = tex3D(tex, vec3(%s.x, %s.y, u_mipBias))%s;\n", texcoord, texcoord, bgraTexture ? ".bgra" : "");
}
} else {
WRITE(p, " vec4 t = tex2D(tex, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : "");
if (doTextureProjection) {
WRITE(p, " vec4 t = tex2Dproj(tex, vec4(v_texcoord.x, v_texcoord.y, 0.0, v_texcoord.z))%s;\n", bgraTexture ? ".bgra" : "");
} else {
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);
// Note that here we're relying on the filter to be linear. We would have to otherwise to do two samples and manually filter in Z.
// Let's add that if we run into a case...
if (texture3D) {
if (doTextureProjection) {
WRITE(p, " vec4 t = %sProj(tex, vec4(%s.xy, u_mipBias, %s.z));\n", compat.texture3D, texcoord, texcoord);
} else {
WRITE(p, " vec4 t = %s(tex, vec3(%s.xy, u_mipBias));\n", compat.texture3D, 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 {
if (doTextureProjection) {
// We don't use textureProj because we need better control and it's probably not much of a savings anyway.
@ -572,7 +586,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");

View file

@ -34,8 +34,9 @@ struct FShaderID;
#define CONST_PS_FBOTEXSIZE 7
#define CONST_PS_TEXCLAMP 8
#define CONST_PS_TEXCLAMPOFF 9
#define CONST_PS_MIPBIAS 10
// For stencil upload
#define CONST_PS_STENCILVALUE 10
#define CONST_PS_STENCILVALUE 11
bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, std::string *errorString);

View file

@ -87,11 +87,13 @@ enum : uint64_t {
DIRTY_DEPAL = 1ULL << 35,
DIRTY_COLORWRITEMASK = 1ULL << 36,
DIRTY_MIPBIAS = 1ULL << 37,
// space for 4 more uniform dirty flags. Remember to update DIRTY_ALL_UNIFORMS.
DIRTY_BONE_UNIFORMS = 0xFF000000ULL,
DIRTY_ALL_UNIFORMS = 0x1FFFFFFFFFULL,
DIRTY_ALL_UNIFORMS = 0x3FFFFFFFFFULL,
DIRTY_ALL_LIGHTS = DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3,
// Other dirty elements that aren't uniforms!

View file

@ -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);

View file

@ -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) {

View file

@ -106,6 +106,11 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
ub->texClampOffset[1] = gstate_c.curTextureYOffset * invH;
}
if (dirtyUniforms & DIRTY_MIPBIAS) {
float mipBias = (float)gstate.getTexLevelOffset16() * (1.0 / 16.0f);
ub->mipBias = (mipBias + 0.5f) / (float)(gstate.getTextureMaxLevel() + 1);
}
if (dirtyUniforms & DIRTY_PROJMATRIX) {
Matrix4x4 flippedMatrix;
memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float));

View file

@ -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;

View file

@ -162,6 +162,7 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac
key.sClamp = gstate.isTexCoordClampedS();
key.tClamp = gstate.isTexCoordClampedT();
key.aniso = false;
key.texture3d = gstate_c.curTextureIs3D;
GETexLevelMode mipMode = gstate.getTexLevelMode();
bool autoMip = mipMode == GE_TEXLEVEL_MODE_AUTO;
@ -481,7 +482,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 +494,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;
@ -535,7 +538,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
const AttachCandidate &candidate = candidates[index];
nextTexture_ = nullptr;
nextNeedsRebuild_ = false;
SetTextureFramebuffer(candidate);
SetTextureFramebuffer(candidate); // sets curTexture3D
return nullptr;
}
}
@ -597,6 +600,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_) {
@ -1069,6 +1073,8 @@ void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate)
nextTexture_ = nullptr;
}
gstate_c.SetTextureIs3D(false);
nextNeedsRehash_ = false;
nextNeedsChange_ = false;
nextNeedsRebuild_ = false;
@ -1132,6 +1138,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 +1301,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.
@ -1663,10 +1681,13 @@ CheckAlphaResult TextureCacheCommon::ReadIndexedTex(u8 *out, int outPitch, int l
texptr = (u8 *)tmpTexBuf32_.data();
}
const bool mipmapShareClut = gstate.isClutSharedForMipmaps();
const int clutSharingOffset = mipmapShareClut ? 0 : (level & 1) * 256;
GEPaletteFormat palFormat = (GEPaletteFormat)gstate.getClutPaletteFormat();
const u16 *clut16 = (const u16 *)clutBuf_;
const u32 *clut32 = (const u32 *)clutBuf_;
const u16 *clut16 = (const u16 *)clutBuf_ + clutSharingOffset;
const u32 *clut32 = (const u32 *)clutBuf_ + clutSharingOffset;
if (expandTo32Bit && palFormat != GE_CMODE_32BIT_ABGR8888) {
ConvertFormatToRGBA8888(GEPaletteFormat(palFormat), expandClut_, clut16, 256);
@ -1706,6 +1727,7 @@ CheckAlphaResult TextureCacheCommon::ReadIndexedTex(u8 *out, int outPitch, int l
case GE_CMODE_32BIT_ABGR8888:
{
switch (bytesPerIndex) {
case 1:
for (int y = 0; y < h; ++y) {
@ -1751,6 +1773,9 @@ void TextureCacheCommon::ApplyTexture() {
ApplyTextureFramebuffer(nextFramebufferTexture_, gstate.getTextureFormat(), depth ? NOTIFY_FB_DEPTH : NOTIFY_FB_COLOR);
nextFramebufferTexture_ = nullptr;
}
// We don't set the 3D texture state here or anything else, on some backends (?)
// a nextTexture_ of nullptr means keep the current texture.
return;
}
@ -1805,6 +1830,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 +2081,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 +2091,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 && draw_->GetDeviceCaps().texture3DSupported) {
plan.depth = plan.levelsToLoad;
plan.scaleFactor = 1;
}
plan.levelsToLoad = 1;
plan.levelsToCreate = 1;
}
if (plan.hardwareScaling) {
@ -2079,7 +2122,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;

View file

@ -71,6 +71,7 @@ struct SamplerCacheKey {
bool sClamp : 1;
bool tClamp : 1;
bool aniso : 1;
bool texture3d : 1;
};
};
bool operator < (const SamplerCacheKey &other) const {
@ -135,6 +136,8 @@ struct TexCacheEntry {
STATUS_FRAMEBUFFER_OVERLAP = 0x1000,
STATUS_FORCE_REBUILD = 0x2000,
STATUS_3D = 0x4000,
};
// Status, but int so we can zero initialize.
@ -264,6 +267,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 +333,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);

View file

@ -335,6 +335,8 @@ void DrawEngineD3D11::DoFlush() {
// until critical state changes. That's when we draw (flush).
GEPrimitiveType prim = prevPrim_;
// SetTexture is called in here, along with setting a lot of other state.
ApplyDrawState(prim);
// Always use software for flat shading to fix the provoking index.
@ -508,7 +510,7 @@ rotateVBO:
prim = indexGen.Prim();
}
VERBOSE_LOG(G3D, "Flush prim %i! %i verts in one go", prim, vertexCount);
VERBOSE_LOG(G3D, "Flush prim %d! %d verts in one go", prim, vertexCount);
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);

View file

@ -467,24 +467,46 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
}
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
int levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
D3D11_TEXTURE2D_DESC desc{};
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Width = tw;
desc.Height = th;
desc.Format = dstFmt;
desc.MipLevels = levels;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
int levels;;
ID3D11ShaderResourceView *view;
ID3D11Texture2D *texture = DxTex(entry);
ID3D11Resource *texture = DxTex(entry);
_assert_(texture == nullptr);
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &texture));
if (plan.depth == 1) {
ID3D11Texture2D *tex;
D3D11_TEXTURE2D_DESC desc{};
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Width = tw;
desc.Height = th;
desc.Format = dstFmt;
desc.MipLevels = plan.levelsToCreate;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &tex));
texture = tex;
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
} else {
ID3D11Texture3D *tex;
D3D11_TEXTURE3D_DESC desc{};
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.Width = tw;
desc.Height = th;
desc.Depth = plan.depth;
desc.Format = dstFmt;
desc.MipLevels = 1;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ASSERT_SUCCESS(device_->CreateTexture3D(&desc, nullptr, &tex));
texture = tex;
levels = plan.depth;
}
ASSERT_SUCCESS(device_->CreateShaderResourceView(texture, nullptr, &view));
entry->texturePtr = texture;
entry->textureView = view;
@ -525,11 +547,24 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, false);
ID3D11Texture2D *texture = DxTex(entry);
context_->UpdateSubresource(texture, i, nullptr, data, stride, 0);
if (plan.depth == 1) {
context_->UpdateSubresource(texture, i, nullptr, data, stride, 0);
} else {
D3D11_BOX box{};
box.front = i;
box.back = i + 1;
box.right = w * plan.scaleFactor;
box.bottom = h * plan.scaleFactor;
context_->UpdateSubresource(texture, 0, &box, data, stride, 0);
}
FreeAlignedMemory(data);
}
// Signal that we support depth textures so use it as one.
if (plan.depth > 1) {
entry->status |= TexCacheEntry::STATUS_3D;
}
if (levels == 1) {
entry->status |= TexCacheEntry::STATUS_NO_MIPS;
} else {

View file

@ -77,8 +77,8 @@ private:
ID3D11Device *device_;
ID3D11DeviceContext *context_;
ID3D11Texture2D *&DxTex(TexCacheEntry *entry) {
return (ID3D11Texture2D *&)entry->texturePtr;
ID3D11Resource *&DxTex(TexCacheEntry *entry) {
return (ID3D11Resource *&)entry->texturePtr;
}
ID3D11ShaderResourceView *DxView(TexCacheEntry *entry) {
return (ID3D11ShaderResourceView *)entry->textureView;

View file

@ -263,7 +263,7 @@ static void ConvertProjMatrixToD3DThrough(Matrix4x4 &in) {
in.translateAndScale(Vec3(xoff, yoff, 0.5f), Vec3(1.0f, 1.0f, 0.5f));
}
const uint64_t psUniforms = DIRTY_TEXENV | DIRTY_ALPHACOLORREF | DIRTY_ALPHACOLORMASK | DIRTY_FOGCOLOR | DIRTY_STENCILREPLACEVALUE | DIRTY_SHADERBLEND | DIRTY_TEXCLAMP;
const uint64_t psUniforms = DIRTY_TEXENV | DIRTY_ALPHACOLORREF | DIRTY_ALPHACOLORMASK | DIRTY_FOGCOLOR | DIRTY_STENCILREPLACEVALUE | DIRTY_SHADERBLEND | DIRTY_TEXCLAMP | DIRTY_MIPBIAS;
void ShaderManagerDX9::PSUpdateUniforms(u64 dirtyUniforms) {
if (dirtyUniforms & DIRTY_TEXENV) {
@ -315,6 +315,14 @@ void ShaderManagerDX9::PSUpdateUniforms(u64 dirtyUniforms) {
PSSetFloatArray(CONST_PS_TEXCLAMP, texclamp, 4);
PSSetFloatArray(CONST_PS_TEXCLAMPOFF, texclampoff, 2);
}
if (dirtyUniforms & DIRTY_MIPBIAS) {
float mipBias = (float)gstate.getTexLevelOffset16() * (1.0 / 16.0f);
// NOTE: This equation needs some adjustment in D3D9. Can't get it to look completely smooth :(
mipBias = (mipBias + 0.25f) / (float)(gstate.getTextureMaxLevel() + 1);
PSSetFloatArray(CONST_PS_MIPBIAS, &mipBias, 1);
}
}
const uint64_t vsUniforms = DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_WORLDMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX |

View file

@ -98,7 +98,7 @@ void TextureCacheDX9::SetFramebufferManager(FramebufferManagerDX9 *fbManager) {
}
void TextureCacheDX9::ReleaseTexture(TexCacheEntry *entry, bool delete_them) {
LPDIRECT3DTEXTURE9 &texture = DxTex(entry);
LPDIRECT3DBASETEXTURE9 &texture = DxTex(entry);
if (texture) {
texture->Release();
texture = nullptr;
@ -205,7 +205,7 @@ void TextureCacheDX9::UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase
}
void TextureCacheDX9::BindTexture(TexCacheEntry *entry) {
LPDIRECT3DTEXTURE9 texture = DxTex(entry);
LPDIRECT3DBASETEXTURE9 texture = DxTex(entry);
if (texture != lastBoundTexture) {
device_->SetTexture(0, texture);
lastBoundTexture = texture;
@ -425,10 +425,20 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) {
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
int levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
LPDIRECT3DTEXTURE9 &texture = DxTex(entry);
LPDIRECT3DBASETEXTURE9 &texture = DxTex(entry);
D3DPOOL pool = D3DPOOL_DEFAULT;
int usage = D3DUSAGE_DYNAMIC;
HRESULT hr = device_->CreateTexture(tw, th, levels, usage, dstFmt, pool, &texture, NULL);
HRESULT hr;
if (plan.depth == 1) {
LPDIRECT3DTEXTURE9 tex;
hr = device_->CreateTexture(tw, th, levels, usage, dstFmt, pool, &tex, nullptr);
texture = tex;
} else {
LPDIRECT3DVOLUMETEXTURE9 tex;
hr = device_->CreateVolumeTexture(tw, th, plan.depth, 1, usage, dstFmt, pool, &tex, nullptr);
texture = tex;
}
if (FAILED(hr)) {
INFO_LOG(G3D, "Failed to create D3D texture: %dx%d", tw, th);
@ -443,24 +453,45 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) {
Draw::DataFormat texFmt = FromD3D9Format(dstFmt);
// Mipmapping is only enabled when texture scaling is disabled.
for (int i = 0; i < levels; i++) {
int dstLevel = i;
HRESULT result;
uint32_t lockFlag = dstLevel == 0 ? D3DLOCK_DISCARD : 0; // Can only discard the top level
D3DLOCKED_RECT rect{};
result = texture->LockRect(dstLevel, &rect, NULL, lockFlag);
if (plan.depth == 1) {
// Regular loop.
for (int i = 0; i < levels; i++) {
int dstLevel = i;
HRESULT result;
uint32_t lockFlag = dstLevel == 0 ? D3DLOCK_DISCARD : 0; // Can only discard the top level
D3DLOCKED_RECT rect{};
result = ((LPDIRECT3DTEXTURE9)texture)->LockRect(dstLevel, &rect, NULL, lockFlag);
if (FAILED(result)) {
ERROR_LOG(G3D, "Failed to lock D3D 2D texture at level %d: %dx%d", i, plan.w, plan.h);
return;
}
uint8_t *data = (uint8_t *)rect.pBits;
int stride = rect.Pitch;
LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, texFmt, false);
((LPDIRECT3DTEXTURE9)texture)->UnlockRect(dstLevel);
}
} else {
// 3D loop.
D3DLOCKED_BOX box;
HRESULT result = ((LPDIRECT3DVOLUMETEXTURE9)texture)->LockBox(0, &box, nullptr, D3DLOCK_DISCARD);
if (FAILED(result)) {
ERROR_LOG(G3D, "Failed to lock D3D texture at level %d: %dx%d", i, plan.w, plan.h);
ERROR_LOG(G3D, "Failed to lock D3D 2D texture: %dx%dx%d", plan.w, plan.h, plan.depth);
return;
}
uint8_t *data = (uint8_t *)rect.pBits;
int stride = rect.Pitch;
uint8_t *data = (uint8_t *)box.pBits;
int stride = box.RowPitch;
for (int i = 0; i < plan.depth; i++) {
LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, texFmt, false);
data += box.SlicePitch;
}
((LPDIRECT3DVOLUMETEXTURE9)texture)->UnlockBox(0);
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, texFmt, false);
texture->UnlockRect(dstLevel);
// Signal that we support depth textures so use it as one.
if (plan.depth > 1) {
entry->status |= TexCacheEntry::STATUS_3D;
}
if (plan.replaced->Valid()) {

View file

@ -68,8 +68,8 @@ private:
void ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, GETextureFormat texFormat, FramebufferNotificationChannel channel) override;
void BuildTexture(TexCacheEntry *const entry) override;
LPDIRECT3DTEXTURE9 &DxTex(TexCacheEntry *entry) {
return *(LPDIRECT3DTEXTURE9 *)&entry->texturePtr;
LPDIRECT3DBASETEXTURE9 &DxTex(TexCacheEntry *entry) {
return *(LPDIRECT3DBASETEXTURE9 *)&entry->texturePtr;
}
LPDIRECT3DDEVICE9 device_;
@ -77,7 +77,7 @@ private:
LPDIRECT3DVERTEXDECLARATION9 pFramebufferVertexDecl;
LPDIRECT3DTEXTURE9 lastBoundTexture;
LPDIRECT3DBASETEXTURE9 lastBoundTexture;
float maxAnisotropyLevel;
FramebufferManagerDX9 *framebufferManagerDX9_;

View file

@ -98,11 +98,11 @@ GLRTexture *DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, con
int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512;
DepalTexture *tex = new DepalTexture();
tex->texture = render_->CreateTexture(GL_TEXTURE_2D, texturePixels, 1, 1);
tex->texture = render_->CreateTexture(GL_TEXTURE_2D, texturePixels, 1, 1, 1);
uint8_t *clutCopy = new uint8_t[1024];
memcpy(clutCopy, rawClut, 1024);
render_->TextureImage(tex->texture, 0, texturePixels, 1, dstFmt, clutCopy, GLRAllocType::NEW, false);
render_->TextureImage(tex->texture, 0, texturePixels, 1, 1, dstFmt, clutCopy, GLRAllocType::NEW, false);
tex->lastFrame = gpuStats.numFlips;
texCache_[clutId] = tex;

View file

@ -502,8 +502,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p
prevSizeU = size_u;
prevSizeV = size_v;
if (!data_tex[0])
data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D, size_u * 3, size_v, 1);
renderManager_->TextureImage(data_tex[0], 0, size_u * 3, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D, size_u * 3, size_v, 1, 1);
renderManager_->TextureImage(data_tex[0], 0, size_u * 3, size_v, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
renderManager_->FinalizeTexture(data_tex[0], 0, false);
}
renderManager_->BindTexture(TEX_SLOT_SPLINE_POINTS, data_tex[0]);
@ -520,8 +520,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p
if (prevSizeWU < weights.size_u) {
prevSizeWU = weights.size_u;
if (!data_tex[1])
data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_u * 2, 1, 1);
renderManager_->TextureImage(data_tex[1], 0, weights.size_u * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_u * 2, 1, 1, 1);
renderManager_->TextureImage(data_tex[1], 0, weights.size_u * 2, 1, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
renderManager_->FinalizeTexture(data_tex[1], 0, false);
}
renderManager_->BindTexture(TEX_SLOT_SPLINE_WEIGHTS_U, data_tex[1]);
@ -531,8 +531,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p
if (prevSizeWV < weights.size_v) {
prevSizeWV = weights.size_v;
if (!data_tex[2])
data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_v * 2, 1, 1);
renderManager_->TextureImage(data_tex[2], 0, weights.size_v * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_v * 2, 1, 1, 1);
renderManager_->TextureImage(data_tex[2], 0, weights.size_v * 2, 1, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
renderManager_->FinalizeTexture(data_tex[2], 0, false);
}
renderManager_->BindTexture(TEX_SLOT_SPLINE_WEIGHTS_V, data_tex[2]);

View file

@ -144,8 +144,8 @@ GLRTexture *FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4]
}
}
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D, 256, 1, 1);
render_->TextureImage(tex, 0, 256, 1, Draw::DataFormat::R8G8B8A8_UNORM, data);
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D, 256, 1, 1, 1);
render_->TextureImage(tex, 0, 256, 1, 1, Draw::DataFormat::R8G8B8A8_UNORM, data);
return tex;
}

View file

@ -174,6 +174,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
queries.push_back({ &u_tess_weights_v, "u_tess_weights_v" });
queries.push_back({ &u_spline_counts, "u_spline_counts" });
queries.push_back({ &u_depal_mask_shift_off_fmt, "u_depal_mask_shift_off_fmt" });
queries.push_back({ &u_mipBias, "u_mipBias" });
attrMask = vs->GetAttrMask();
availableUniforms = vs->GetUniformMask() | fs->GetUniformMask();
@ -458,6 +459,13 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu
}
}
if ((dirty & DIRTY_MIPBIAS) && u_mipBias != -1) {
float mipBias = (float)gstate.getTexLevelOffset16() * (1.0 / 16.0f);
mipBias = (mipBias + 0.5f) / (float)(gstate.getTextureMaxLevel() + 1);
render_->SetUniformF(&u_mipBias, 1, &mipBias);
}
// Transform
if (dirty & DIRTY_WORLDMATRIX) {
SetMatrix4x3(render_, &u_world, gstate.worldMatrix);

View file

@ -61,6 +61,7 @@ public:
int u_cullRangeMin;
int u_cullRangeMax;
int u_rotation;
int u_mipBias;
#ifdef USE_BONE_ARRAY
int u_bone; // array, size is numBones

View file

@ -451,7 +451,11 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
dstFmt = Draw::DataFormat::R8G8B8A8_UNORM;
}
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, tw, plan.levelsToCreate);
if (plan.depth == 1) {
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, tw, 1, plan.levelsToCreate);
} else {
entry->textureName = render_->CreateTexture(GL_TEXTURE_3D, tw, tw, plan.depth, 1);
}
// Apply some additional compatibility checks.
if (plan.levelsToLoad > 1) {
@ -468,46 +472,68 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
plan.levelsToCreate = plan.levelsToLoad;
}
for (int i = 0; i < plan.levelsToLoad; i++) {
int srcLevel = i == 0 ? plan.baseLevelSrc : i;
if (plan.depth == 1) {
for (int i = 0; i < plan.levelsToLoad; i++) {
int srcLevel = i == 0 ? plan.baseLevelSrc : i;
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
u8 *data = nullptr;
int stride = 0;
u8 *data = nullptr;
int stride = 0;
if (plan.replaced->GetSize(srcLevel, w, h)) {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
stride = w * bpp;
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
} else {
if (plan.scaleFactor > 1) {
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
stride = w * plan.scaleFactor * 4;
} else {
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
stride = std::max(w * bpp, 4);
if (plan.replaced->GetSize(srcLevel, w, h)) {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
stride = w * bpp;
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
} else {
if (plan.scaleFactor > 1) {
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
stride = w * plan.scaleFactor * 4;
} else {
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
stride = std::max(w * bpp, 4);
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
}
}
if (!data) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
return;
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true);
// NOTE: TextureImage takes ownership of data, so we don't free it afterwards.
render_->TextureImage(entry->textureName, i, w * plan.scaleFactor, h * plan.scaleFactor, 1, dstFmt, data, GLRAllocType::ALIGNED);
}
if (!data) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
return;
bool genMips = plan.levelsToCreate > plan.levelsToLoad;
render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips);
} else {
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
int stride = bpp * (plan.w * plan.scaleFactor);
int levelStride = stride * (plan.h * plan.scaleFactor);
u8 *data = (u8 *)AllocateAlignedMemory(levelStride * plan.depth, 16);
memset(data, 0, levelStride * plan.depth);
u8 *p = data;
for (int i = 0; i < plan.depth; i++) {
LoadTextureLevel(*entry, p, stride, *plan.replaced, i, plan.scaleFactor, dstFmt, true);
p += levelStride;
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true);
render_->TextureImage(entry->textureName, 0, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, dstFmt, data, GLRAllocType::ALIGNED);
// NOTE: TextureImage takes ownership of data, so we don't free it afterwards.
render_->TextureImage(entry->textureName, i, w * plan.scaleFactor, h * plan.scaleFactor, dstFmt, data, GLRAllocType::ALIGNED);
// Signal that we support depth textures so use it as one.
entry->status |= TexCacheEntry::STATUS_3D;
render_->FinalizeTexture(entry->textureName, 1, false);
}
bool genMips = plan.levelsToCreate > plan.levelsToLoad;
render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips);
if (plan.replaced->Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}

View file

@ -1560,11 +1560,19 @@ void GPUCommon::Execute_TexLevel(u32 op, u32 diff) {
// TODO: If you change the rules here, don't forget to update the inner interpreter in Execute_Prim.
if (diff == 0xFFFFFFFF)
return;
gstate.texlevel ^= diff;
if (diff & 0xFF0000) {
// Piggyback on this flag for 3D textures.
gstate_c.Dirty(DIRTY_MIPBIAS);
}
if (gstate.getTexLevelMode() != GE_TEXLEVEL_MODE_AUTO && (0x00FF0000 & gstate.texlevel) != 0) {
Flush();
}
gstate.texlevel ^= diff;
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE);
}

View file

@ -300,8 +300,8 @@ struct GPUgstate {
bool isTextureFormatIndexed() const { return (texformat & 4) != 0; } // GE_TFMT_CLUT4 - GE_TFMT_CLUT32 are 0b1xx.
int getTextureEnvColRGB() const { return texenvcolor & 0x00FFFFFF; }
u32 getClutAddress() const { return (clutaddr & 0x00FFFFF0) | ((clutaddrupper << 8) & 0x0F000000); }
int getClutLoadBytes() const { return (loadclut & 0x3F) * 32; }
int getClutLoadBlocks() const { return (loadclut & 0x3F); }
int getClutLoadBytes() const { return (loadclut & 0x7F) * 32; }
int getClutLoadBlocks() const { return (loadclut & 0x7F); }
GEPaletteFormat getClutPaletteFormat() const { return static_cast<GEPaletteFormat>(clutformat & 3); }
int getClutIndexShift() const { return (clutformat >> 2) & 0x1F; }
int getClutIndexMask() const { return (clutformat >> 8) & 0xFF; }
@ -551,6 +551,12 @@ struct GPUStateCache {
Dirty(DIRTY_FRAGMENTSHADER_STATE);
}
}
void SetTextureIs3D(bool is3D) {
if (is3D != curTextureIs3D) {
curTextureIs3D = is3D;
Dirty(DIRTY_FRAGMENTSHADER_STATE | (is3D ? DIRTY_MIPBIAS : 0));
}
}
u32 featureFlags;
@ -580,6 +586,7 @@ struct GPUStateCache {
// Only applied when needShaderTexClamp = true.
u32 curTextureXOffset;
u32 curTextureYOffset;
bool curTextureIs3D;
float vpWidth;
float vpHeight;

View file

@ -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();

View file

@ -119,7 +119,8 @@ VkSampler SamplerCache::GetOrCreateSampler(const SamplerCacheKey &key) {
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
samp.addressModeU = key.sClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT;
samp.addressModeV = key.tClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT;
samp.addressModeW = samp.addressModeU; // irrelevant, but Mali recommends that all clamp modes are the same if possible.
// W addressing is irrelevant for 2d textures, but Mali recommends that all clamp modes are the same if possible so just copy from U.
samp.addressModeW = key.texture3d ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : samp.addressModeU;
samp.compareOp = VK_COMPARE_OP_ALWAYS;
samp.flags = 0;
samp.magFilter = key.magFilt ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
@ -413,7 +414,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS);
bool depth = channel == NOTIFY_FB_DEPTH;
bool useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer && !depth;
bool useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer && !depth && !gstate_c.curTextureIs3D;
bool need_depalettize = IsClutFormat(texFormat);
@ -588,7 +589,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
}
int maxPossibleMipLevels;
if (plan.isVideo) {
if (plan.isVideo || plan.depth != 1) {
maxPossibleMipLevels = 1;
} else {
maxPossibleMipLevels = log2i(std::min(plan.w * plan.scaleFactor, plan.h * plan.scaleFactor)) + 1;
@ -652,7 +653,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 +672,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 +687,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 +701,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 +750,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 +779,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()) {
@ -797,6 +808,11 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
entry->vkTex->EndCreate(cmdInit, false, prevStage, layout);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
// Signal that we support depth textures so use it as one.
if (plan.depth > 1) {
entry->status |= TexCacheEntry::STATUS_3D;
}
if (plan.replaced->Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}