Get untextured drawing working! (at least cube.elf)

This commit is contained in:
Henrik Rydgard 2016-01-09 11:07:14 +01:00
parent 28ae840abe
commit d67d187b72
10 changed files with 113 additions and 62 deletions

View file

@ -1,6 +1,3 @@
#include "GPU/Vulkan/DrawEngineVulkan.h"
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
@ -47,6 +44,14 @@
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/GPU_Vulkan.h"
enum {
DRAW_BINDING_TEXTURE = 0,
DRAW_BINDING_2ND_TEXTURE = 1,
DRAW_BINDING_DYNUBO_BASE = 2,
DRAW_BINDING_DYNUBO_LIGHT = 3,
DRAW_BINDING_DYNUBO_BONE = 4,
};
const VkPrimitiveTopology prim[8] = {
VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
@ -99,27 +104,27 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
bindings[0].pImmutableSamplers = nullptr;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[0].binding = 0;
bindings[0].binding = DRAW_BINDING_TEXTURE;
bindings[1].descriptorCount = 1;
bindings[1].pImmutableSamplers = nullptr;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[1].binding = 1;
bindings[1].binding = DRAW_BINDING_2ND_TEXTURE;
bindings[2].descriptorCount = 1;
bindings[2].pImmutableSamplers = nullptr;
bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[2].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[2].binding = 2;
bindings[2].binding = DRAW_BINDING_DYNUBO_BASE;
bindings[3].descriptorCount = 1;
bindings[3].pImmutableSamplers = nullptr;
bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[3].binding = 3;
bindings[3].binding = DRAW_BINDING_DYNUBO_LIGHT;
bindings[4].descriptorCount = 1;
bindings[4].pImmutableSamplers = nullptr;
bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[4].binding = 4;
bindings[4].binding = DRAW_BINDING_DYNUBO_BONE;
VkDevice device = vulkan_->GetDevice();
@ -131,7 +136,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
VkResult res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_);
VkDescriptorPoolSize dpTypes[2];
dpTypes[0].descriptorCount = 200;
dpTypes[0].descriptorCount = 800;
dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
dpTypes[1].descriptorCount = 200;
dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
@ -139,8 +144,8 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
VkDescriptorPoolCreateInfo dp;
dp.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
dp.pNext = nullptr;
dp.flags = 0; // Don't want to mess around with individually freeing these, let's go dynamic each frame.
dp.maxSets = 200; // 200 textures per frame should be enough for the UI...
dp.flags = 0; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later.
dp.maxSets = 1000;
dp.pPoolSizes = dpTypes;
dp.poolSizeCount = ARRAY_SIZE(dpTypes);
res = vkCreateDescriptorPool(device, &dp, nullptr, &frame_[0].descPool);
@ -167,6 +172,8 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan)
assert(VK_SUCCESS == res);
VkSamplerCreateInfo samp = {};
samp.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samp.pNext = nullptr;
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
@ -377,13 +384,14 @@ inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) {
}
}
VkDescriptorSet DrawEngineVulkan::GetDescriptorSet(CachedTextureVulkan *texture, VkSampler sampler) {
VkDescriptorSet DrawEngineVulkan::GetDescriptorSet(CachedTextureVulkan *texture, VkSampler sampler, VkBuffer dynamicUbo) {
DescriptorSetKey key;
key.texture_ = texture;
key.sampler_ = sampler;
key.secondaryTexture_ = nullptr;
FrameData *frame = &frame_[curFrame_ & 1];
key.buffer_ = dynamicUbo;
FrameData *frame = &frame_[curFrame_ & 1];
auto iter = frame->descSets.find(key);
if (iter != frame->descSets.end()) {
return iter->second;
@ -392,22 +400,59 @@ VkDescriptorSet DrawEngineVulkan::GetDescriptorSet(CachedTextureVulkan *texture,
// Didn't find one in the frame cache, let's make a new one.
VkDescriptorSet desc;
VkDescriptorSetAllocateInfo descAlloc;
descAlloc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descAlloc.pNext = nullptr;
descAlloc.pSetLayouts = &descriptorSetLayout_;
descAlloc.descriptorPool = frame->descPool;
descAlloc.descriptorSetCount = 1;
vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc);
// We just don't write to the slots we don't care about.
VkWriteDescriptorSet writes[2];
VkWriteDescriptorSet writes[4];
memset(writes, 0, sizeof(writes));
// Main texture
VkDescriptorImageInfo tex;
int n = 0;
if (texture) {
VkDescriptorImageInfo tex;
tex.imageLayout = texture->imageLayout;
tex.imageView = texture->imageView;
tex.sampler = sampler;
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[n].pNext = nullptr;
writes[n].dstBinding = DRAW_BINDING_TEXTURE;
writes[n].pImageInfo = &tex;
writes[n].descriptorCount = 1;
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writes[n].dstSet = desc;
n++;
}
// tex.imageView = texture->imageView;
tex.sampler = sampler;
writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[0].dstBinding = 0;
writes[0].pImageInfo = &tex;
vkUpdateDescriptorSets(vulkan_->GetDevice(), 2, writes, 0, nullptr);
// Skipping 2nd texture for now.
// Uniform buffer objects
VkDescriptorBufferInfo buf[3];
buf[0].buffer = dynamicUbo;
buf[0].offset = 0;
buf[0].range = sizeof(UB_VS_FS_Base);
buf[1].buffer = dynamicUbo;
buf[1].offset = 0;
buf[1].range = sizeof(UB_VS_Lights);
buf[2].buffer = dynamicUbo;
buf[2].offset = 0;
buf[2].range = sizeof(UB_VS_Bones);
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[n].pNext = nullptr;
writes[n].dstBinding = DRAW_BINDING_DYNUBO_BASE;
writes[n].pBufferInfo = &buf[0];
writes[n].dstSet = desc;
writes[n].descriptorCount = 3;
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
n++;
vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr);
frame->descSets[key] = desc;
return nullptr;
return desc;
}
// The inline wrapper in the header checks for numDrawCalls == 0d
@ -416,16 +461,19 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
FrameData *frame = &frame_[curFrame_ & 1];
// CachedTextureVulkan *tex = textureCache_->ApplyTexture();
// VkDescriptorSet ds = GetDescriptorSet(tex);
VkDescriptorSet ds;
// Note than when we implement overflow in pushbuffer, we need to make sure to overflow here, not between
// the three ubo pushes. The reason is that the three UBOs must be in the same buffer as that's how we
// designed the descriptor set.
// CachedTextureVulkan *tex = textureCache_->ApplyTexture();
VkDescriptorSet ds = GetDescriptorSet(nullptr, nullptr, frame->pushData->GetVkBuffer());
GEPrimitiveType prim = prevPrim_;
// ApplyDrawState(prim);
bool useHWTransform = CanUseHardwareTransform(prim);
VulkanVertexShader *vshader;
VulkanFragmentShader *fshader;
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader);
uint32_t baseUBOOffset = 0;
uint32_t lightUBOOffset = 0;
@ -434,7 +482,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
uint32_t ibOffset = 0;
uint32_t vbOffset = 0;
if (vshader->UseHWTransform()) {
if (useHWTransform) {
int vertexCount = 0;
int maxIndex = 0;
bool useElements = true;
@ -449,7 +497,6 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
}
prim = indexGen.Prim();
VERBOSE_LOG(G3D, "Flush prim %i! %i 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);
@ -467,6 +514,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
vkCmdSetStencilWriteMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilWriteMask);
vkCmdSetStencilCompareMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilCompareMask);
// vkCmdSetBlendConstants(cmd_, dynState.blendColor);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, pipelineKey, dec_, vshader->GetModule(), fshader->GetModule(), true);
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
@ -481,21 +529,21 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
}
VkBuffer buf[1] = {frame->pushData->GetVkBuffer()};
uint32_t dynamicUBOOffsets[3] = {
const uint32_t dynamicUBOOffsets[3] = {
baseUBOOffset, lightUBOOffset, boneUBOOffset,
};
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
ibOffset = (uint32_t)frame->pushData->Push(decIndex, 2 * indexGen.VertexCount());
vbOffset = (uint32_t)frame->pushData->Push(decoded, vertexCount * dec_->GetDecVtxFmt().stride);
VkDeviceSize offsets[1] = { vbOffset };
if (useElements) {
ibOffset = (uint32_t)frame->pushData->Push(decIndex, 2 * indexGen.VertexCount());
// TODO: Avoid rebinding vertex/index buffers if the vertex size stays the same by using the offset arguments
// Might want to separate vertices out into a different push buffer in that case.
vkCmdBindVertexBuffers(cmd_, 0, 1, buf, offsets);
vkCmdBindIndexBuffer(cmd_, buf[0], ibOffset, VK_INDEX_TYPE_UINT16);
vkCmdDrawIndexed(cmd_, maxIndex + 1, 1, 0, 0, 0);
vkCmdDrawIndexed(cmd_, maxIndex, 1, 0, 0, 0);
} else {
vkCmdBindVertexBuffers(cmd_, 0, 1, buf, offsets);
vkCmdDraw(cmd_, vertexCount, 1, 0, 0);
@ -548,6 +596,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
}
// vkCmdSetBlendConstants(cmd_, dynState.blendColor);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, pipelineKey, dec_, vshader->GetModule(), fshader->GetModule(), false);
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); // TODO: Avoid if same as last draw.
@ -555,17 +604,17 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
baseUBOOffset = shaderManager_->PushBaseBuffer(frame->pushData);
}
uint32_t dynamicUBOOffsets[3] = {
const uint32_t dynamicUBOOffsets[3] = {
baseUBOOffset, lightUBOOffset, boneUBOOffset,
};
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &ds, 3, dynamicUBOOffsets);
ibOffset = (uint32_t)frame->pushData->Push(decIndex, 2 * indexGen.VertexCount());
vbOffset = (uint32_t)frame->pushData->Push(decoded, numTrans * dec_->GetDecVtxFmt().stride);
VkBuffer buf[1] = { frame->pushData->GetVkBuffer() };
VkDeviceSize offsets[1] = { vbOffset };
if (drawIndexed) {
ibOffset = (uint32_t)frame->pushData->Push(decIndex, 2 * indexGen.VertexCount());
// TODO: Have a buffer per frame, use a walking buffer pointer
// TODO: Avoid rebinding if the vertex size stays the same by using the offset arguments
vkCmdBindVertexBuffers(cmd_, 0, 1, buf, offsets);

View file

@ -149,7 +149,7 @@ private:
void DecodeVertsStep();
void DoFlush(VkCommandBuffer cmd);
VkDescriptorSet GetDescriptorSet(CachedTextureVulkan *texture, VkSampler sampler);
VkDescriptorSet GetDescriptorSet(CachedTextureVulkan *texture, VkSampler sampler, VkBuffer dynamicUbo);
VertexDecoder *GetVertexDecoder(u32 vtype);
@ -163,6 +163,7 @@ private:
void *texture_;
void *secondaryTexture_;
VkSampler sampler_;
VkBuffer buffer_; // All three UBO slots will be set to this.
bool operator < (const DescriptorSetKey &other) const {
if (texture_ < other.texture_) return true; else if (texture_ > other.texture_) return false;

View file

@ -283,6 +283,10 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VkShaderModule vShader, VkShaderModule fShader, bool useHwTransform) {
VulkanPipelineKey key;
key.raster = rasterKey;
key.useHWTransform = useHwTransform;
key.vShader = vShader;
key.fShader = fShader;
key.vtxDec = vtxDec;
auto iter = pipelines_.find(key);
if (iter != pipelines_.end()) {
return iter->second;

View file

@ -37,20 +37,16 @@ enum class PspAttributeLocation {
COUNT
};
// All the information needed. All PSP rendering (except full screen clears?) will make use of a single
// render pass definition.
struct VulkanPipelineKey {
VulkanPipelineRasterStateKey raster;
int prim;
bool pretransformed;
VulkanPipelineRasterStateKey raster; // prim is included here
bool useHWTransform;
const VertexDecoder *vtxDec;
VkShaderModule vShader;
VkShaderModule fShader;
bool operator < (const VulkanPipelineKey &other) const {
if (raster < other.raster) return true; else if (other.raster < raster) return false;
if (prim < other.prim) return true; else if (other.prim < prim) return false;
if (pretransformed < other.pretransformed) return true; else if (other.pretransformed < pretransformed) return false;
if (useHWTransform < other.useHWTransform) return true; else if (other.useHWTransform < useHWTransform) return false;
if (vtxDec < other.vtxDec) return true; else if (other.vtxDec < vtxDec) return false;
if (vShader < other.vShader) return true; else if (other.vShader < vShader) return false;
if (fShader < other.fShader) return true; else if (other.fShader < fShader) return false;

View file

@ -351,7 +351,7 @@ void ShaderManagerVulkan::BaseUpdateUniforms(int dirtyUniforms) {
default:
ERROR_LOG_REPORT(G3D, "Unexpected UV gen mode: %d", gstate.getUVGenMode());
}
}
CopyFloat4(ub_base.uvScaleOffset, uvscaleoff);
}
@ -475,9 +475,7 @@ void ShaderManagerVulkan::DirtyLastShader() { // disables vertex arrays
}
void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader) {
bool useHWTransform = CanUseHardwareTransform(prim);
void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform) {
ShaderID VSID;
ComputeVertexShaderID(&VSID, vertType, useHWTransform);
ShaderID FSID;

View file

@ -81,7 +81,7 @@ enum {
DIRTY_ALL = 0xFFFFFFFF
};
struct UB_VS_TransformCommon {
struct UB_VS_FS_Base {
float proj[16];
float view[16];
float world[16];
@ -107,7 +107,7 @@ R"( mat4 proj_mtx;
mat4 view_mtx;
mat4 world_mtx;
mat4 tex_mtx;
vec4 uvScaleOffset;
vec4 uvscaleoffset;
vec4 depthRange;
vec2 fogCoef;
vec4 matambientalpha;
@ -213,7 +213,7 @@ public:
~ShaderManagerVulkan();
void ClearCache(bool deleteThem); // TODO: deleteThem currently not respected
void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader);
void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, bool useHWTransform);
void DirtyShader();
void DirtyUniform(u32 what) {
@ -251,7 +251,7 @@ private:
char *codeBuffer_;
// Uniform block scratchpad. These (the relevant ones) are copied to the current pushbuffer at draw time.
UB_VS_TransformCommon ub_base;
UB_VS_FS_Base ub_base;
UB_VS_Lights ub_lights;
UB_VS_Bones ub_bones;

View file

@ -188,6 +188,9 @@ void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, int prim, Vulk
bool wantCull = !gstate.isModeThrough() && prim != GE_PRIM_RECTANGLES && gstate.isCullEnabled();
key.cullMode = wantCull ? (gstate.getCullMode() ? VK_CULL_MODE_FRONT_BIT : VK_CULL_MODE_BACK_BIT) : VK_CULL_MODE_NONE;
// TODO: Remove (For debugging)
key.cullMode = VK_CULL_MODE_NONE;
// Depth Test
if (gstate.isDepthTestEnabled()) {
key.depthTestEnable = true;

View file

@ -68,12 +68,6 @@
// Hack!
extern int g_iNumVideos;
// texturePtr points to these.
class CachedTextureVulkan {
public:
VkImageView imageView;
};
SamplerCache::~SamplerCache() {
for (auto iter : cache_) {
vulkan_->QueueDelete(iter.second);

View file

@ -47,12 +47,19 @@ struct SamplerCacheKey {
}
};
// texturePtr points to these.
class CachedTextureVulkan {
public:
VkImageView imageView;
VkImageLayout imageLayout;
};
class SamplerCache {
public:
SamplerCache(VulkanContext *vulkan) : vulkan_(vulkan) {}
~SamplerCache();
VkSampler GetOrCreateSampler(const SamplerCacheKey &key);
private:
VulkanContext *vulkan_;
std::map<SamplerCacheKey, VkSampler> cache_;

View file

@ -384,13 +384,12 @@ private:
VkFramebuffer framebuffer_;
};
// Use these to push vertex, index and uniform data.
// Use these to push vertex, index and uniform data. Generally you'll have two of these
// and alternate on each frame.
// TODO: Make it possible to suballocate pushbuffers from a large DeviceMemory block.
// TODO: Make this dynamically grow by chaining new buffers in the future.
// Until then, we cap at a maximum size.
// We'll have two of these that we alternate between on each frame.
// These will only be used for the "Thin3D" system - the PSP emulation etc will have
// their own similar buffer solutions.
// TODO: Make this auto-grow and shrink. Need to be careful about returning and using the new
// buffer handle on overflow.
class VulkanPushBuffer {
public:
VulkanPushBuffer(VulkanContext *vulkan, size_t size) : offset_(0), size_(size), writePtr_(nullptr), deviceMemory_(nullptr) {