mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-21 02:09:05 +02:00
TextureCacheVulkan: Move the texture scaling upload shader and passes to common
So the Metal backend can use them too. No functional change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0978a3e363
commit
55109b39fb
4 files changed
+115
-96
No files matched your search
@@ -275,3 +275,89 @@ void ClutTextureCache::Decimate() {
|
||||
}
|
||||
gpuStats.perFrame.numClutTextures = (int)texCache_.size();
|
||||
}
|
||||
|
||||
const char *const textureScaleUploadShader = R"(
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
// 8x8 is the most common compute shader workgroup size, and works great on all major
|
||||
// hardware vendors. TODO: However, we should probably change to 16x16, as Qualcomm now has
|
||||
// support for bigger groups...
|
||||
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
uniform layout(set = 0, binding = 0, rgba8) writeonly image2D img;
|
||||
|
||||
layout(std430, set = 0, binding = 1) buffer Buf {
|
||||
uint data[];
|
||||
} buf;
|
||||
|
||||
layout(push_constant) uniform Params {
|
||||
int width;
|
||||
int height;
|
||||
} params;
|
||||
|
||||
// The cbuffer, if present, is self-declared for layout flexibility
|
||||
#define CBUFFER_SET 0
|
||||
#define CBUFFER_BINDING 4
|
||||
|
||||
uint readColoru(uvec2 p) {
|
||||
return buf.data[p.y * params.width + p.x];
|
||||
}
|
||||
|
||||
vec4 readColorf(uvec2 p) {
|
||||
// Unpack the color (we could look it up in a CLUT here if we wanted...)
|
||||
// The imageStore repack is free.
|
||||
return unpackUnorm4x8(readColoru(p));
|
||||
}
|
||||
|
||||
void writeColorf(ivec2 p, vec4 c) {
|
||||
imageStore(img, p, c);
|
||||
}
|
||||
|
||||
%s
|
||||
|
||||
// Note that main runs once per INPUT pixel, unlike the old model.
|
||||
void main() {
|
||||
uvec2 xy = gl_GlobalInvocationID.xy;
|
||||
// Kill off any out-of-image threads to avoid stray writes.
|
||||
// Should only happen on the tiniest mipmaps as PSP textures are power-of-2,
|
||||
// and we use a 8x8 workgroup size. Probably not really necessary.
|
||||
if (xy.x >= params.width || xy.y >= params.height)
|
||||
return;
|
||||
// applyScaling will write the upscaled pixels, using writeColorf above.
|
||||
// It's expected to write a square of scale*scale pixels, at the location xy*scale.
|
||||
applyScaling(xy);
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
bool GetTextureScaleMultipassLayout(int scaleFactor, std::vector<TextureScaleScratchDesc> *scratchDescs, std::vector<TextureScaleStageDesc> *stageDescs) {
|
||||
if (scaleFactor == 2) {
|
||||
*scratchDescs = {
|
||||
{ "nnedi3_vertical", 1, 2 },
|
||||
{ "nnedi3_horizontal", 2, 2 },
|
||||
};
|
||||
*stageDescs = {
|
||||
{ 0, -1, 0, 1, 1, 1, 2, false },
|
||||
{ 1, 0, 1, 1, 2, 2, 2, false },
|
||||
{ 2, 1, -1, 2, 2, 2, 2, false },
|
||||
};
|
||||
} else if (scaleFactor == 4) {
|
||||
*scratchDescs = {
|
||||
{ "nnedi3_vertical", 1, 2 },
|
||||
{ "nnedi3_horizontal", 2, 2 },
|
||||
{ "nnedi3_vertical2", 2, 4 },
|
||||
{ "nnedi3_horizontal2", 4, 4 },
|
||||
};
|
||||
*stageDescs = {
|
||||
{ 0, -1, 0, 1, 1, 1, 2, false },
|
||||
{ 2, 0, 1, 1, 2, 2, 2, false },
|
||||
{ 1, 1, 2, 2, 2, 2, 4, false },
|
||||
{ 2, 2, 3, 2, 4, 4, 4, false },
|
||||
{ 3, 3, -1, 4, 4, 4, 4, true },
|
||||
};
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -77,3 +77,28 @@ private:
|
||||
Draw2D *draw2D_;
|
||||
std::unordered_map<u64, Draw2DPipeline *> pipelineCache_;
|
||||
};
|
||||
|
||||
// Hardware texture upscaling, with compute shaders.
|
||||
|
||||
// The single pass upload shader, with a %s for the scaling shader (its applyScaling()).
|
||||
extern const char *const textureScaleUploadShader;
|
||||
|
||||
struct TextureScaleScratchDesc {
|
||||
const char *tag;
|
||||
uint8_t widthScale;
|
||||
uint8_t heightScale;
|
||||
};
|
||||
|
||||
struct TextureScaleStageDesc {
|
||||
uint8_t shaderIndex;
|
||||
int8_t inputScratch; // -1 means the original upload buffer.
|
||||
int8_t outputScratch; // -1 means the final destination image.
|
||||
uint8_t srcWidthScale;
|
||||
uint8_t srcHeightScale;
|
||||
uint8_t dstWidthScale;
|
||||
uint8_t dstHeightScale;
|
||||
bool useFinalOutputSize;
|
||||
};
|
||||
|
||||
// The scratch images and passes of a multipass scaling shader (NNEDI3). False for an unsupported scale factor.
|
||||
bool GetTextureScaleMultipassLayout(int scaleFactor, std::vector<TextureScaleScratchDesc> *scratchDescs, std::vector<TextureScaleStageDesc> *stageDescs);
|
||||
@@ -55,60 +55,6 @@ using namespace PPSSPP_VK;
|
||||
#define TEXCACHE_MAX_SLAB_SIZE (32 * 1024 * 1024)
|
||||
#define TEXCACHE_SLAB_PRESSURE 4
|
||||
|
||||
const char *uploadShader = R"(
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
// 8x8 is the most common compute shader workgroup size, and works great on all major
|
||||
// hardware vendors. TODO: However, we should probably change to 16x16, as Qualcomm now has
|
||||
// support for bigger groups...
|
||||
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
uniform layout(set = 0, binding = 0, rgba8) writeonly image2D img;
|
||||
|
||||
layout(std430, set = 0, binding = 1) buffer Buf {
|
||||
uint data[];
|
||||
} buf;
|
||||
|
||||
layout(push_constant) uniform Params {
|
||||
int width;
|
||||
int height;
|
||||
} params;
|
||||
|
||||
// The cbuffer, if present, is self-declared for layout flexibility
|
||||
#define CBUFFER_SET 0
|
||||
#define CBUFFER_BINDING 4
|
||||
|
||||
uint readColoru(uvec2 p) {
|
||||
return buf.data[p.y * params.width + p.x];
|
||||
}
|
||||
|
||||
vec4 readColorf(uvec2 p) {
|
||||
// Unpack the color (we could look it up in a CLUT here if we wanted...)
|
||||
// The imageStore repack is free.
|
||||
return unpackUnorm4x8(readColoru(p));
|
||||
}
|
||||
|
||||
void writeColorf(ivec2 p, vec4 c) {
|
||||
imageStore(img, p, c);
|
||||
}
|
||||
|
||||
%s
|
||||
|
||||
// Note that main runs once per INPUT pixel, unlike the old model.
|
||||
void main() {
|
||||
uvec2 xy = gl_GlobalInvocationID.xy;
|
||||
// Kill off any out-of-image threads to avoid stray writes.
|
||||
// Should only happen on the tiniest mipmaps as PSP textures are power-of-2,
|
||||
// and we use a 8x8 workgroup size. Probably not really necessary.
|
||||
if (xy.x >= params.width || xy.y >= params.height)
|
||||
return;
|
||||
// applyScaling will write the upscaled pixels, using writeColorf above.
|
||||
// It's expected to write a square of scale*scale pixels, at the location xy*scale.
|
||||
applyScaling(xy);
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
static int VkFormatBytesPerPixel(VkFormat format) {
|
||||
switch (format) {
|
||||
@@ -301,31 +247,7 @@ bool TextureCacheVulkan::CompileMultipassShader(VulkanContext *vulkan, const Tex
|
||||
if (!shaderInfo.computeShaderFiles.empty()) {
|
||||
shaderFiles = shaderInfo.computeShaderFiles;
|
||||
}
|
||||
if (shaderInfo.scaleFactor == 2) {
|
||||
multipassScratchDescs_ = {
|
||||
{ "nnedi3_vertical", 1, 2 },
|
||||
{ "nnedi3_horizontal", 2, 2 },
|
||||
};
|
||||
multipassStageDescs_ = {
|
||||
{ 0, -1, 0, 1, 1, 1, 2, false },
|
||||
{ 1, 0, 1, 1, 2, 2, 2, false },
|
||||
{ 2, 1, -1, 2, 2, 2, 2, false },
|
||||
};
|
||||
} else if (shaderInfo.scaleFactor == 4) {
|
||||
multipassScratchDescs_ = {
|
||||
{ "nnedi3_vertical", 1, 2 },
|
||||
{ "nnedi3_horizontal", 2, 2 },
|
||||
{ "nnedi3_vertical2", 2, 4 },
|
||||
{ "nnedi3_horizontal2", 4, 4 },
|
||||
};
|
||||
multipassStageDescs_ = {
|
||||
{ 0, -1, 0, 1, 1, 1, 2, false },
|
||||
{ 2, 0, 1, 1, 2, 2, 2, false },
|
||||
{ 1, 1, 2, 2, 2, 2, 4, false },
|
||||
{ 2, 2, 3, 2, 4, 4, 4, false },
|
||||
{ 3, 3, -1, 4, 4, 4, 4, true },
|
||||
};
|
||||
} else {
|
||||
if (!GetTextureScaleMultipassLayout(shaderInfo.scaleFactor, &multipassScratchDescs_, &multipassStageDescs_)) {
|
||||
ERROR_LOG(Log::G3D, "Unsupported multipass scale factor %d in section '%s'", shaderInfo.scaleFactor, shaderInfo.section.c_str());
|
||||
return false;
|
||||
}
|
||||
@@ -395,7 +317,7 @@ void TextureCacheVulkan::CompileScalingShader() {
|
||||
}
|
||||
} else {
|
||||
std::string shaderSource = ReadShaderSrc(shaderInfo->computeShaderFiles[0]);
|
||||
std::string fullUploadShader = StringFromFormat(uploadShader, shaderSource.c_str());
|
||||
std::string fullUploadShader = StringFromFormat(textureScaleUploadShader, shaderSource.c_str());
|
||||
singlePassCS_ = CompileShaderModule(vulkan, VK_SHADER_STAGE_COMPUTE_BIT, fullUploadShader.c_str(), &error);
|
||||
_dbg_assert_msg_(singlePassCS_ != VK_NULL_HANDLE, "failed to compile upload shader");
|
||||
if (singlePassCS_ == VK_NULL_HANDLE)
|
||||
|
||||
@@ -101,22 +101,8 @@ private:
|
||||
MULTIPASS,
|
||||
};
|
||||
|
||||
struct MultipassScratchDesc {
|
||||
const char *tag;
|
||||
uint8_t widthScale;
|
||||
uint8_t heightScale;
|
||||
};
|
||||
|
||||
struct MultipassStageDesc {
|
||||
uint8_t shaderIndex;
|
||||
int8_t inputScratch; // -1 means the original upload buffer.
|
||||
int8_t outputScratch; // -1 means the final destination image.
|
||||
uint8_t srcWidthScale;
|
||||
uint8_t srcHeightScale;
|
||||
uint8_t dstWidthScale;
|
||||
uint8_t dstHeightScale;
|
||||
bool useFinalOutputSize;
|
||||
};
|
||||
typedef TextureScaleScratchDesc MultipassScratchDesc;
|
||||
typedef TextureScaleStageDesc MultipassStageDesc;
|
||||
|
||||
void LoadVulkanTextureLevel(TexCacheEntry &entry, uint8_t *writePtr, int rowPitch, int level, int scaleFactor, VkFormat dstFmt);
|
||||
static VkFormat GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) ;
|
||||
|
||||
Reference in new issue
Block a user