Permalink
Browse files

Merge pull request #10038 from hrydgard/vulkan-depal

Vulkan: Implement framebuffer "depal" (palette lookup) and shader blending
  • Loading branch information...
hrydgard committed Oct 31, 2017
2 parents 0c7ae95 + bbfc32f commit 980e1734c2c39208a129483e812320ff5f83d93e
@@ -269,6 +269,8 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips,
0, VK_ACCESS_TRANSFER_WRITE_BIT);
break;
default:
// If you planned to use UploadMip, you want VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. After the
// upload, you can transition.
assert(false);
break;
}
@@ -44,7 +44,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang
WRITE(p, "layout(set = 0, binding = 0) uniform sampler2D tex;\n");
WRITE(p, "layout(set = 0, binding = 1) uniform sampler2D pal;\n");
WRITE(p, "layout(location = 0) in vec2 v_texcoord0;\n");
WRITE(p, "layout(location = 0) out vec4 fragColor0\n;");
WRITE(p, "layout(location = 0) out vec4 fragColor0;\n");
} else {
if (gl_extensions.IsGLES) {
WRITE(p, "#version 300 es\n");
@@ -288,4 +288,4 @@ void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguag
}
}
#undef WRITE
#undef WRITE
@@ -20,4 +20,6 @@
#include "GPU/ge_constants.h"
#include "GPU/Common/ShaderCommon.h"
static const int DEPAL_TEXTURE_OLD_AGE = 120;
void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language);
@@ -1806,8 +1806,6 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(u16 w, u16 h, Draw::FBCo
if (!fbo)
return fbo;
// TODO: Move binding out of here!
draw_->BindFramebufferAsRenderTarget(fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
const TempFBO info = { fbo, gpuStats.numFlips };
tempFBOs_[key] = info;
return fbo;
@@ -29,8 +29,6 @@
#include "GPU/D3D11/D3D11Util.h"
#include "GPU/Common/DepalettizeShaderCommon.h"
static const int DEPAL_TEXTURE_OLD_AGE = 120;
#ifdef _WIN32
#define SHADERLOG
#endif
@@ -26,8 +26,6 @@
#include "ext/native/gfx/GLStateCache.h"
#include "GPU/Common/DepalettizeShaderCommon.h"
static const int DEPAL_TEXTURE_OLD_AGE = 120;
#ifdef _WIN32
#define SHADERLOG
#endif
@@ -15,33 +15,143 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Common/Vulkan/VulkanContext.h"
#include "GPU/GPUState.h"
#include "GPU/Common/DepalettizeShaderCommon.h"
#include "GPU/Vulkan/DepalettizeShaderVulkan.h"
#include "GPU/Vulkan/VulkanUtil.h"
#include "Common/Vulkan/VulkanImage.h"
static const char depal_vs[] = R"(#version 400
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout (location = 0) in vec3 a_position;
layout (location = 1) in vec2 a_texcoord0;
layout (location = 0) out vec2 v_texcoord0;
out gl_PerVertex { vec4 gl_Position; };
void main() {
v_texcoord0 = a_texcoord0;
gl_Position = vec4(a_position, 1.0);
}
)";
static VkFormat GetClutDestFormat(GEPaletteFormat format) {
switch (format) {
case GE_CMODE_16BIT_ABGR4444:
return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
case GE_CMODE_16BIT_ABGR5551:
return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
case GE_CMODE_16BIT_BGR5650:
return VK_FORMAT_R5G6B5_UNORM_PACK16;
case GE_CMODE_32BIT_ABGR8888:
return VK_FORMAT_R8G8B8A8_UNORM;
}
return VK_FORMAT_UNDEFINED;
}
DepalShaderCacheVulkan::DepalShaderCacheVulkan() {
DepalShaderCacheVulkan::DepalShaderCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan)
: draw_(draw), vulkan_(vulkan) {
std::string errors;
vshader_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, depal_vs, &errors);
assert(vshader_ != VK_NULL_HANDLE);
}
DepalShaderCacheVulkan::~DepalShaderCacheVulkan() {
Clear();
vulkan_->Delete().QueueDeleteShaderModule(vshader_);
}
DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) {
return nullptr;
DepalShaderVulkan *DepalShaderCacheVulkan::GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat) {
u32 id = GenerateShaderID(clutMode, pixelFormat);
auto shader = cache_.find(id);
if (shader != cache_.end()) {
return shader->second;
}
VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS);
char *buffer = new char[2048];
GenerateDepalShader(buffer, pixelFormat, GLSL_VULKAN);
std::string error;
VkShaderModule fshader = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, buffer, &error);
if (fshader == VK_NULL_HANDLE) {
Crash();
delete[] buffer;
return nullptr;
}
VkPipeline pipeline = vulkan2D_->GetPipeline(rp, vshader_, fshader);
// Can delete the shader module now that the pipeline has been created.
// Maybe don't even need to queue it..
vulkan_->Delete().QueueDeleteShaderModule(fshader);
DepalShaderVulkan *depal = new DepalShaderVulkan();
depal->pipeline = pipeline;
depal->code = buffer;
cache_[id] = depal;
return depal;
}
VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut) {
return nullptr;
VulkanTexture *DepalShaderCacheVulkan::GetClutTexture(GEPaletteFormat clutFormat, u32 clutID, u32 *rawClut) {
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
const u32 realClutID = clutID ^ clutFormat;
auto oldtex = texCache_.find(realClutID);
if (oldtex != texCache_.end()) {
oldtex->second->lastFrame = gpuStats.numFlips;
return oldtex->second->texture;
}
VkFormat destFormat = GetClutDestFormat(clutFormat);
int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512;
VkBuffer pushBuffer;
uint32_t pushOffset = push_->PushAligned(rawClut, 1024, 4, &pushBuffer);
VulkanTexture *vktex = new VulkanTexture(vulkan_, alloc_);
if (!vktex->CreateDirect(cmd, texturePixels, 1, 1, destFormat,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, nullptr)) {
Crash();
}
vktex->UploadMip(cmd, 0, texturePixels, 1, pushBuffer, pushOffset, texturePixels);
vktex->EndCreate(cmd);
DepalTextureVulkan *tex = new DepalTextureVulkan();
tex->texture = vktex;
tex->lastFrame = gpuStats.numFlips;
texCache_[realClutID] = tex;
return tex->texture;
}
void DepalShaderCacheVulkan::Clear() {}
void DepalShaderCacheVulkan::Decimate() {}
void DepalShaderCacheVulkan::Clear() {
for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) {
// Don't need to destroy the pipelines, they're handled by Vulkan2D.
delete shader->second;
}
cache_.clear();
for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) {
delete tex->second->texture;
delete tex->second;
}
texCache_.clear();
}
u32 DepalShaderCacheVulkan::GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat) {
return 0;
void DepalShaderCacheVulkan::Decimate() {
// We don't bother decimating the generated shaders, there are never very many of them.
for (auto tex = texCache_.begin(); tex != texCache_.end(); ) {
if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) {
delete tex->second->texture;
delete tex->second;
texCache_.erase(tex++);
} else {
++tex;
}
}
}
bool DepalShaderCacheVulkan::CreateVertexShader() {
return false;
u32 DepalShaderCacheVulkan::GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat) {
return (clutMode & 0xFFFFFF) | (pixelFormat << 24);
}
@@ -20,42 +20,58 @@
#include <map>
#include "Common/CommonTypes.h"
#include "Common/Vulkan/VulkanContext.h"
#include "Common/Vulkan/VulkanImage.h"
#include "Common/Vulkan/VulkanMemory.h"
#include "GPU/ge_constants.h"
#include "thin3d/thin3d.h"
class DepalShaderVulkan {
public:
/*
GLuint program;
GLuint fragShader;
GLint a_position;
GLint a_texcoord0;
*/
~DepalShaderVulkan() {
delete[] code;
}
// A Vulkan2D pipeline. Set texture to slot 0 and palette texture to slot 1.
VkPipeline pipeline = VK_NULL_HANDLE;
const char *code = nullptr;;
};
class DepalTextureVulkan {
public:
int texture;
VulkanTexture *texture = nullptr;
int lastFrame;
};
class VulkanTexture;
class Vulkan2D;
// Caches both shaders and palette textures.
// Could even avoid bothering with palette texture and just use uniform data...
class DepalShaderCacheVulkan {
public:
DepalShaderCacheVulkan();
DepalShaderCacheVulkan(Draw::DrawContext *draw, VulkanContext *vulkan);
~DepalShaderCacheVulkan();
// This also uploads the palette and binds the correct texture.
DepalShaderVulkan *GetDepalettizeShader(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat);
DepalShaderVulkan *GetDepalettizeShader(uint32_t clutMode, GEBufferFormat pixelFormat);
VulkanTexture *GetClutTexture(GEPaletteFormat clutFormat, const u32 clutHash, u32 *rawClut);
void Clear();
void Decimate();
void SetVulkan2D(Vulkan2D *vk2d) { vulkan2D_ = vk2d; }
void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; }
void SetAllocator(VulkanDeviceAllocator *alloc) { alloc_ = alloc; }
void SetVShader(VkShaderModule vshader) { vshader_ = vshader; }
private:
u32 GenerateShaderID(GEPaletteFormat clutFormat, GEBufferFormat pixelFormat);
bool CreateVertexShader();
u32 GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat);
Draw::DrawContext *draw_ = nullptr;
VulkanContext *vulkan_ = nullptr;
VulkanPushBuffer *push_ = nullptr;
VulkanDeviceAllocator *alloc_ = nullptr;
VkShaderModule vshader_ = VK_NULL_HANDLE;
Vulkan2D *vulkan2D_ = nullptr;
// GLuint vertexShader_;
std::map<u32, DepalShaderVulkan *> cache_;
@@ -202,7 +202,7 @@ void DrawEngineVulkan::InitDeviceObjects() {
samp.flags = 0;
samp.magFilter = VK_FILTER_NEAREST;
samp.minFilter = VK_FILTER_NEAREST;
res = vkCreateSampler(device, &samp, nullptr, &depalSampler_);
res = vkCreateSampler(device, &samp, nullptr, &samplerSecondary_);
res = vkCreateSampler(device, &samp, nullptr, &nullSampler_);
assert(VK_SUCCESS == res);
@@ -245,8 +245,8 @@ void DrawEngineVulkan::DestroyDeviceObjects() {
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
frame_[i].Destroy(vulkan_);
}
if (depalSampler_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteSampler(depalSampler_);
if (samplerSecondary_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteSampler(samplerSecondary_);
if (nullSampler_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteSampler(nullSampler_);
if (pipelineLayout_ != VK_NULL_HANDLE)
@@ -507,12 +507,11 @@ void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset,
}
}
VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone) {
DescriptorSetKey key;
key.imageView_ = imageView;
key.sampler_ = sampler;
key.secondaryImageView_ = VK_NULL_HANDLE;
key.secondaryImageView_ = boundSecondary_;
key.base_ = base;
key.light_ = light;
key.bone_ = bone;
@@ -561,6 +560,21 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
n++;
}
if (boundSecondary_) {
// TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them?
tex.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
tex.imageView = boundSecondary_;
tex.sampler = samplerSecondary_;
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[n].pNext = nullptr;
writes[n].dstBinding = DRAW_BINDING_2ND_TEXTURE;
writes[n].pImageInfo = &tex;
writes[n].descriptorCount = 1;
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writes[n].dstSet = desc;
n++;
}
// Skipping 2nd texture for now.
// Tessellation data textures
@@ -897,14 +911,12 @@ void DrawEngineVulkan::DoFlush() {
UpdateUBOs(frame);
VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf);
{
PROFILE_THIS_SCOPE("renderman_q");
const uint32_t dynamicUBOOffsets[3] = {
baseUBOOffset, lightUBOOffset, boneUBOOffset,
};
// vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
int stride = dec_->GetDecVtxFmt().stride;
@@ -179,6 +179,7 @@ class DrawEngineVulkan : public DrawEngineCommon {
struct FrameData;
void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant);
void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState);
void ResetShaderBlending();
void InitDeviceObjects();
void DestroyDeviceObjects();
@@ -200,6 +201,10 @@ class DrawEngineVulkan : public DrawEngineCommon {
VulkanPipeline *lastPipeline_;
VkDescriptorSet lastDs_ = VK_NULL_HANDLE;
// Secondary texture for shader blending
VkImageView boundSecondary_ = VK_NULL_HANDLE;
VkSampler samplerSecondary_ = VK_NULL_HANDLE;
PrehashMap<VertexArrayInfoVulkan *, nullptr> vai_;
VulkanPushBuffer *vertexCache_;
int decimationCounter_ = 0;
@@ -237,16 +242,14 @@ class DrawEngineVulkan : public DrawEngineCommon {
TextureCacheVulkan *textureCache_ = nullptr;
FramebufferManagerVulkan *framebufferManager_ = nullptr;
VkSampler depalSampler_;
// State cache
uint64_t dirtyUniforms_;
uint32_t baseUBOOffset;
uint32_t lightUBOOffset;
uint32_t boneUBOOffset;
VkBuffer baseBuf, lightBuf, boneBuf;
VkImageView imageView;
VkSampler sampler;
VkImageView imageView = VK_NULL_HANDLE;
VkSampler sampler = VK_NULL_HANDLE;
// Null texture
VulkanTexture *nullTexture_ = nullptr;
Oops, something went wrong.

0 comments on commit 980e173

Please sign in to comment.