generate mipmaps for custom textures when needed

This commit is contained in:
Flyinghead 2020-03-09 22:49:05 +01:00
parent 015dcf117d
commit 03f096384b
6 changed files with 83 additions and 22 deletions

View File

@ -636,7 +636,7 @@ void BaseTextureCacheData::Update()
need_32bit_buffer = false;
// TODO avoid upscaling/depost. textures that change too often
bool mipmapped = IsMipmapped() && settings.rend.UseMipmaps && !settings.rend.DumpTextures;
bool mipmapped = IsMipmapped() && !settings.rend.DumpTextures;
if (texconv32 != NULL && need_32bit_buffer)
{
@ -753,7 +753,7 @@ void BaseTextureCacheData::Update()
//lock the texture to detect changes in it
lock_block = libCore_vramlock_Lock(sa_tex,sa+size-1,this);
UploadToGPU(upscaled_w, upscaled_h, (u8*)temp_tex_buffer, mipmapped);
UploadToGPU(upscaled_w, upscaled_h, (u8*)temp_tex_buffer, mipmapped, mipmapped);
if (settings.rend.DumpTextures)
{
ComputeHash();
@ -768,7 +768,7 @@ void BaseTextureCacheData::CheckCustomTexture()
if (IsCustomTextureAvailable())
{
tex_type = TextureType::_8888;
UploadToGPU(custom_width, custom_height, custom_image_data, false);
UploadToGPU(custom_width, custom_height, custom_image_data, IsMipmapped(), false);
delete [] custom_image_data;
custom_image_data = NULL;
}

View File

@ -689,7 +689,7 @@ struct BaseTextureCacheData
bool IsMipmapped()
{
return tcw.MipMapped != 0 && tcw.ScanOrder == 0;
return tcw.MipMapped != 0 && tcw.ScanOrder == 0 && settings.rend.UseMipmaps;
}
const char* GetPixelFormatName()
@ -715,7 +715,7 @@ struct BaseTextureCacheData
void Create();
void ComputeHash();
void Update();
virtual void UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped) = 0;
virtual void UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped, bool mipmapsIncluded = false) = 0;
virtual bool Force32BitTexture(TextureType type) const { return false; }
void CheckCustomTexture();
//true if : dirty or paletted texture and hashes don't match

View File

@ -200,7 +200,7 @@ struct TextureCacheData : BaseTextureCacheData
{
GLuint texID; //gl texture
virtual std::string GetId() override { return std::to_string(texID); }
virtual void UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped) override;
virtual void UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped, bool mipmapsIncluded = false) override;
virtual bool Delete() override;
};

View File

@ -69,7 +69,7 @@ static void dumpRtTexture(u32 name, u32 w, u32 h) {
free(rows);
}
void TextureCacheData::UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped)
void TextureCacheData::UploadToGPU(int width, int height, u8 *temp_tex_buffer, bool mipmapped, bool mipmapsIncluded)
{
//upload to OpenGL !
glcache.BindTexture(GL_TEXTURE_2D, texID);
@ -94,7 +94,7 @@ void TextureCacheData::UploadToGPU(int width, int height, u8 *temp_tex_buffer, b
die("Unsupported texture type");
break;
}
if (mipmapped)
if (mipmapsIncluded)
{
int mipmapLevels = 0;
int dim = width;
@ -149,9 +149,9 @@ void TextureCacheData::UploadToGPU(int width, int height, u8 *temp_tex_buffer, b
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0,comps, width, height, 0, comps, gltype, temp_tex_buffer);
if (mipmapped)
glGenerateMipmap(GL_TEXTURE_2D);
}
glCheck();
}

View File

@ -143,7 +143,7 @@ void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk:
commandBuffer.pipelineBarrier(sourceStage, destinationStage, {}, nullptr, nullptr, imageMemoryBarrier);
}
void Texture::UploadToGPU(int width, int height, u8 *data, bool mipmapped)
void Texture::UploadToGPU(int width, int height, u8 *data, bool mipmapped, bool mipmapsIncluded)
{
vk::Format format;
u32 dataSize = width * height * 2;
@ -167,7 +167,7 @@ void Texture::UploadToGPU(int width, int height, u8 *data, bool mipmapped)
dataSize /= 2;
break;
}
if (mipmapped)
if (mipmapsIncluded)
{
int w = width / 2;
u32 size = dataSize / 4;
@ -180,13 +180,13 @@ void Texture::UploadToGPU(int width, int height, u8 *data, bool mipmapped)
}
bool isNew = true;
if (width != extent.width || height != extent.height || format != this->format)
Init(width, height, format, dataSize, mipmapped);
Init(width, height, format, dataSize, mipmapped, mipmapsIncluded);
else
isNew = false;
SetImage(dataSize, data, isNew);
SetImage(dataSize, data, isNew, mipmapped && !mipmapsIncluded);
}
void Texture::Init(u32 width, u32 height, vk::Format format, u32 dataSize, bool mipmapped)
void Texture::Init(u32 width, u32 height, vk::Format format, u32 dataSize, bool mipmapped, bool mipmapsIncluded)
{
this->extent = vk::Extent2D(width, height);
this->format = format;
@ -217,6 +217,8 @@ void Texture::Init(u32 width, u32 height, vk::Format format, u32 dataSize, bool
initialLayout = vk::ImageLayout::ePreinitialized;
requirements = vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible;
}
if (mipmapped && !mipmapsIncluded)
usageFlags |= vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
CreateImage(imageTiling, usageFlags, initialLayout, requirements, vk::ImageAspectFlagBits::eColor);
}
@ -238,7 +240,7 @@ void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk:
imageView = device.createImageViewUnique(imageViewCreateInfo);
}
void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
void Texture::SetImage(u32 srcSize, void *srcData, bool isNew, bool genMipmaps)
{
verify((bool)commandBuffer);
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
@ -261,7 +263,8 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
// Since we're going to blit to the texture image, set its layout to eTransferDstOptimal
setImageLayout(commandBuffer, image.get(), format, mipmapLevels, isNew ? vk::ImageLayout::eUndefined : vk::ImageLayout::eShaderReadOnlyOptimal,
vk::ImageLayout::eTransferDstOptimal);
if (mipmapLevels > 1)
if (mipmapLevels > 1 && !genMipmaps)
{
vk::DeviceSize bufferOffset = 0;
for (int i = 0; i < mipmapLevels; i++)
@ -277,18 +280,75 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
vk::BufferImageCopy copyRegion(0, extent.width, extent.height, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1),
vk::Offset3D(0, 0, 0), vk::Extent3D(extent, 1));
commandBuffer.copyBufferToImage(stagingBufferData->buffer.get(), image.get(), vk::ImageLayout::eTransferDstOptimal, copyRegion);
if (mipmapLevels > 1)
GenerateMipmaps();
}
// Set the layout for the texture image from eTransferDstOptimal to SHADER_READ_ONLY
setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
}
else
{
// If we can use the linear tiled image as a texture, just do it
setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal);
if (mipmapLevels > 1)
GenerateMipmaps();
else
// If we can use the linear tiled image as a texture, just do it
setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal);
}
commandBuffer.end();
}
void Texture::GenerateMipmaps()
{
u32 mipWidth = extent.width;
u32 mipHeight = extent.height;
vk::ImageMemoryBarrier barrier(vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eTransferRead,
vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eTransferSrcOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
*image, vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
for (int i = 1; i < mipmapLevels; i++)
{
// Transition previous mipmap level from dst optimal/preinit to src optimal
barrier.subresourceRange.baseMipLevel = i - 1;
if (i == 1 && !needsStaging)
{
barrier.oldLayout = vk::ImageLayout::ePreinitialized;
barrier.srcAccessMask = vk::AccessFlagBits::eHostWrite;
}
else
{
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
}
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, nullptr, nullptr, barrier);
// Blit previous mipmap level on current
vk::ImageBlit blit(vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i - 1, 0, 1),
{ { vk::Offset3D(0, 0, 0), vk::Offset3D(mipWidth, mipHeight, 1) } },
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i, 0, 1),
{ { vk::Offset3D(0, 0, 0), vk::Offset3D(std::max(mipWidth / 2, 1u), std::max(mipHeight / 2, 1u), 1) } });
commandBuffer.blitImage(*image, vk::ImageLayout::eTransferSrcOptimal, *image, vk::ImageLayout::eTransferDstOptimal, 1, &blit, vk::Filter::eLinear);
// Transition previous mipmap level from src optimal to shader read-only optimal
barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr, barrier);
mipWidth = std::max(mipWidth / 2, 1u);
mipHeight = std::max(mipHeight / 2, 1u);
}
// Transition last mipmap level from dst optimal to shader read-only optimal
barrier.subresourceRange.baseMipLevel = mipmapLevels - 1;
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr, barrier);
}
void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, vk::ImageUsageFlags usage)
{
this->format = format;

View File

@ -30,7 +30,7 @@ void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk:
struct Texture : BaseTextureCacheData
{
void UploadToGPU(int width, int height, u8 *data, bool mipmapped) override;
void UploadToGPU(int width, int height, u8 *data, bool mipmapped, bool mipmapsIncluded = false) override;
u64 GetIntId() { return (u64)reinterpret_cast<uintptr_t>(this); }
std::string GetId() override { char s[20]; sprintf(s, "%p", this); return s; }
bool IsNew() const { return !image.get(); }
@ -43,10 +43,11 @@ struct Texture : BaseTextureCacheData
void SetDevice(vk::Device device) { this->device = device; }
private:
void Init(u32 width, u32 height, vk::Format format ,u32 dataSize, bool mipmapped);
void SetImage(u32 size, void *data, bool isNew);
void Init(u32 width, u32 height, vk::Format format ,u32 dataSize, bool mipmapped, bool mipmapsIncluded);
void SetImage(u32 size, void *data, bool isNew, bool genMipmaps);
void CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout,
vk::MemoryPropertyFlags memoryProperties, vk::ImageAspectFlags aspectMask);
void GenerateMipmaps();
vk::Format format = vk::Format::eUndefined;
vk::Extent2D extent;