From 97174dbe4d69ad1ecfa1b270219868a10caee86e Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Fri, 19 Feb 2016 23:23:58 -0800 Subject: [PATCH] Skeleton leaky hacky hardcoded pipeline setup. --- src/xenia/gpu/vulkan/buffer_cache.h | 3 + src/xenia/gpu/vulkan/pipeline_cache.cc | 379 +++++++++++++++++- src/xenia/gpu/vulkan/pipeline_cache.h | 26 +- src/xenia/gpu/vulkan/render_cache.cc | 2 + src/xenia/gpu/vulkan/render_cache.h | 2 + src/xenia/gpu/vulkan/texture_cache.cc | 50 ++- src/xenia/gpu/vulkan/texture_cache.h | 9 + .../gpu/vulkan/vulkan_command_processor.cc | 57 +-- .../gpu/vulkan/vulkan_command_processor.h | 3 + 9 files changed, 495 insertions(+), 36 deletions(-) diff --git a/src/xenia/gpu/vulkan/buffer_cache.h b/src/xenia/gpu/vulkan/buffer_cache.h index af42f23d8..9a264a80b 100644 --- a/src/xenia/gpu/vulkan/buffer_cache.h +++ b/src/xenia/gpu/vulkan/buffer_cache.h @@ -38,6 +38,9 @@ class BufferCache { VkDescriptorSet constant_descriptor_set() const { return transient_descriptor_set_; } + VkDescriptorSetLayout constant_descriptor_set_layout() const { + return descriptor_set_layout_; + } // Uploads the constants specified in the register maps to the transient // uniform storage buffer. diff --git a/src/xenia/gpu/vulkan/pipeline_cache.cc b/src/xenia/gpu/vulkan/pipeline_cache.cc index 5416aea7f..aca0d72b5 100644 --- a/src/xenia/gpu/vulkan/pipeline_cache.cc +++ b/src/xenia/gpu/vulkan/pipeline_cache.cc @@ -23,11 +23,64 @@ namespace vulkan { using xe::ui::vulkan::CheckResult; -PipelineCache::PipelineCache(RegisterFile* register_file, - ui::vulkan::VulkanDevice* device) - : register_file_(register_file), device_(*device) {} +PipelineCache::PipelineCache( + RegisterFile* register_file, ui::vulkan::VulkanDevice* device, + VkDescriptorSetLayout uniform_descriptor_set_layout, + VkDescriptorSetLayout texture_descriptor_set_layout) + : register_file_(register_file), device_(*device) { + // Initialize the shared driver pipeline cache. + // We'll likely want to serialize this and reuse it, if that proves to be + // useful. If the shaders are expensive and this helps we could do it per + // game, otherwise a single shared cache for render state/etc. + VkPipelineCacheCreateInfo pipeline_cache_info; + pipeline_cache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + pipeline_cache_info.pNext = nullptr; + pipeline_cache_info.flags = 0; + pipeline_cache_info.initialDataSize = 0; + pipeline_cache_info.pInitialData = nullptr; + auto err = vkCreatePipelineCache(device_, &pipeline_cache_info, nullptr, + &pipeline_cache_); + CheckResult(err, "vkCreatePipelineCache"); + + // Descriptors used by the pipelines. + // These are the only ones we can ever bind. + VkDescriptorSetLayout set_layouts[] = { + // Per-draw constant register uniforms. + uniform_descriptor_set_layout, + // All texture bindings. + texture_descriptor_set_layout, + }; + + // Push constants used for draw parameters. + // We need to keep these under 128b across all stages. + VkPushConstantRange push_constant_ranges[2]; + push_constant_ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constant_ranges[0].offset = 0; + push_constant_ranges[0].size = sizeof(float) * 16; + push_constant_ranges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + push_constant_ranges[1].offset = sizeof(float) * 16; + push_constant_ranges[1].size = sizeof(int); + + // Shared pipeline layout. + VkPipelineLayoutCreateInfo pipeline_layout_info; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.pNext = nullptr; + pipeline_layout_info.flags = 0; + pipeline_layout_info.setLayoutCount = + static_cast(xe::countof(set_layouts)); + pipeline_layout_info.pSetLayouts = set_layouts; + pipeline_layout_info.pushConstantRangeCount = + static_cast(xe::countof(push_constant_ranges)); + pipeline_layout_info.pPushConstantRanges = push_constant_ranges; + err = vkCreatePipelineLayout(*device, &pipeline_layout_info, nullptr, + &pipeline_layout_); + CheckResult(err, "vkCreatePipelineLayout"); +} PipelineCache::~PipelineCache() { + vkDestroyPipelineLayout(device_, pipeline_layout_, nullptr); + vkDestroyPipelineCache(device_, pipeline_cache_, nullptr); + // Destroy all shaders. for (auto it : shader_map_) { delete it.second; @@ -88,13 +141,331 @@ bool PipelineCache::ConfigurePipeline(VkCommandBuffer command_buffer, VulkanShader* vertex_shader, VulkanShader* pixel_shader, PrimitiveType primitive_type) { - return false; + // Uh, yeah. This happened. + + VkPipelineShaderStageCreateInfo pipeline_stages[3]; + uint32_t pipeline_stage_count = 0; + auto& vertex_pipeline_stage = pipeline_stages[pipeline_stage_count++]; + vertex_pipeline_stage.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertex_pipeline_stage.pNext = nullptr; + vertex_pipeline_stage.flags = 0; + vertex_pipeline_stage.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertex_pipeline_stage.module = vertex_shader->shader_module(); + vertex_pipeline_stage.pName = "main"; + vertex_pipeline_stage.pSpecializationInfo = nullptr; + auto geometry_shader = GetGeometryShader(primitive_type); + if (geometry_shader) { + auto& geometry_pipeline_stage = pipeline_stages[pipeline_stage_count++]; + geometry_pipeline_stage.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + geometry_pipeline_stage.pNext = nullptr; + geometry_pipeline_stage.flags = 0; + geometry_pipeline_stage.stage = VK_SHADER_STAGE_GEOMETRY_BIT; + geometry_pipeline_stage.module = geometry_shader; + geometry_pipeline_stage.pName = "main"; + geometry_pipeline_stage.pSpecializationInfo = nullptr; + } + auto& pixel_pipeline_stage = pipeline_stages[pipeline_stage_count++]; + pixel_pipeline_stage.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + pixel_pipeline_stage.pNext = nullptr; + pixel_pipeline_stage.flags = 0; + pixel_pipeline_stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + pixel_pipeline_stage.module = pixel_shader->shader_module(); + pixel_pipeline_stage.pName = "main"; + pixel_pipeline_stage.pSpecializationInfo = nullptr; + + VkPipelineVertexInputStateCreateInfo vertex_state_info; + vertex_state_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_state_info.pNext = nullptr; + VkVertexInputBindingDescription vertex_binding_descrs[64]; + uint32_t vertex_binding_count = 0; + VkVertexInputAttributeDescription vertex_attrib_descrs[64]; + uint32_t vertex_attrib_count = 0; + for (const auto& vertex_binding : vertex_shader->vertex_bindings()) { + assert_true(vertex_binding_count < xe::countof(vertex_binding_descrs)); + auto& vertex_binding_descr = vertex_binding_descrs[vertex_binding_count++]; + vertex_binding_descr.binding = vertex_binding.binding_index; + vertex_binding_descr.stride = vertex_binding.stride_words * 4; + vertex_binding_descr.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + for (const auto& attrib : vertex_binding.attributes) { + assert_true(vertex_attrib_count < xe::countof(vertex_attrib_descrs)); + auto& vertex_attrib_descr = vertex_attrib_descrs[vertex_attrib_count++]; + vertex_attrib_descr.location = attrib.attrib_index; + vertex_attrib_descr.binding = vertex_binding.binding_index; + vertex_attrib_descr.format = VK_FORMAT_UNDEFINED; + vertex_attrib_descr.offset = attrib.fetch_instr.attributes.offset * 4; + + bool is_signed = attrib.fetch_instr.attributes.is_signed; + bool is_integer = attrib.fetch_instr.attributes.is_integer; + switch (attrib.fetch_instr.attributes.data_format) { + case VertexFormat::k_8_8_8_8: + vertex_attrib_descr.format = + is_signed ? VK_FORMAT_R8G8B8A8_SNORM : VK_FORMAT_R8G8B8A8_UNORM; + break; + case VertexFormat::k_2_10_10_10: + vertex_attrib_descr.format = is_signed + ? VK_FORMAT_A2R10G10B10_SNORM_PACK32 + : VK_FORMAT_A2R10G10B10_UNORM_PACK32; + break; + case VertexFormat::k_10_11_11: + assert_always("unsupported?"); + vertex_attrib_descr.format = VK_FORMAT_B10G11R11_UFLOAT_PACK32; + break; + case VertexFormat::k_11_11_10: + assert_true(is_signed); + vertex_attrib_descr.format = VK_FORMAT_B10G11R11_UFLOAT_PACK32; + break; + case VertexFormat::k_16_16: + vertex_attrib_descr.format = + is_signed ? VK_FORMAT_R16G16_SNORM : VK_FORMAT_R16G16_UNORM; + break; + case VertexFormat::k_16_16_FLOAT: + vertex_attrib_descr.format = + is_signed ? VK_FORMAT_R16G16_SSCALED : VK_FORMAT_R16G16_USCALED; + break; + case VertexFormat::k_16_16_16_16: + vertex_attrib_descr.format = is_signed ? VK_FORMAT_R16G16B16A16_SNORM + : VK_FORMAT_R16G16B16A16_UNORM; + break; + case VertexFormat::k_16_16_16_16_FLOAT: + vertex_attrib_descr.format = is_signed + ? VK_FORMAT_R16G16B16A16_SSCALED + : VK_FORMAT_R16G16B16A16_USCALED; + break; + case VertexFormat::k_32: + vertex_attrib_descr.format = + is_signed ? VK_FORMAT_R32_SINT : VK_FORMAT_R32_UINT; + break; + case VertexFormat::k_32_32: + vertex_attrib_descr.format = + is_signed ? VK_FORMAT_R32G32_SINT : VK_FORMAT_R32G32_UINT; + break; + case VertexFormat::k_32_32_32_32: + vertex_attrib_descr.format = + is_signed ? VK_FORMAT_R32G32B32A32_SINT : VK_FORMAT_R32_UINT; + break; + case VertexFormat::k_32_FLOAT: + assert_true(is_signed); + vertex_attrib_descr.format = VK_FORMAT_R32_SFLOAT; + break; + case VertexFormat::k_32_32_FLOAT: + assert_true(is_signed); + vertex_attrib_descr.format = VK_FORMAT_R32G32_SFLOAT; + break; + case VertexFormat::k_32_32_32_FLOAT: + assert_true(is_signed); + vertex_attrib_descr.format = VK_FORMAT_R32G32B32_SFLOAT; + break; + case VertexFormat::k_32_32_32_32_FLOAT: + assert_true(is_signed); + vertex_attrib_descr.format = VK_FORMAT_R32G32B32A32_SFLOAT; + break; + default: + assert_unhandled_case(attrib.fetch_instr.attributes.data_format); + break; + } + } + } + vertex_state_info.vertexBindingDescriptionCount = vertex_binding_count; + vertex_state_info.pVertexBindingDescriptions = vertex_binding_descrs; + vertex_state_info.vertexAttributeDescriptionCount = vertex_attrib_count; + vertex_state_info.pVertexAttributeDescriptions = vertex_attrib_descrs; + + VkPipelineInputAssemblyStateCreateInfo input_info; + input_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_info.pNext = nullptr; + input_info.flags = 0; + input_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_info.primitiveRestartEnable = VK_FALSE; + + VkPipelineViewportStateCreateInfo viewport_state_info; + viewport_state_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state_info.pNext = nullptr; + viewport_state_info.flags = 0; + VkViewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = 100; + viewport.height = 100; + viewport.minDepth = 0; + viewport.maxDepth = 1; + viewport_state_info.viewportCount = 1; + viewport_state_info.pViewports = &viewport; + VkRect2D scissor; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent.width = 100; + scissor.extent.height = 100; + viewport_state_info.scissorCount = 1; + viewport_state_info.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterization_info; + rasterization_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_info.pNext = nullptr; + rasterization_info.flags = 0; + rasterization_info.depthClampEnable = VK_FALSE; + rasterization_info.rasterizerDiscardEnable = VK_FALSE; + rasterization_info.polygonMode = VK_POLYGON_MODE_FILL; + rasterization_info.cullMode = VK_CULL_MODE_BACK_BIT; + rasterization_info.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterization_info.depthBiasEnable = VK_FALSE; + rasterization_info.depthBiasConstantFactor = 0; + rasterization_info.depthBiasClamp = 0; + rasterization_info.depthBiasSlopeFactor = 0; + rasterization_info.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo multisample_info; + multisample_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_info.pNext = nullptr; + multisample_info.flags = 0; + multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisample_info.sampleShadingEnable = VK_FALSE; + multisample_info.minSampleShading = 0; + multisample_info.pSampleMask = nullptr; + multisample_info.alphaToCoverageEnable = VK_FALSE; + multisample_info.alphaToOneEnable = VK_FALSE; + + VkPipelineDepthStencilStateCreateInfo depth_stencil_info; + depth_stencil_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depth_stencil_info.pNext = nullptr; + depth_stencil_info.flags = 0; + depth_stencil_info.depthTestEnable = VK_FALSE; + depth_stencil_info.depthWriteEnable = VK_FALSE; + depth_stencil_info.depthCompareOp = VK_COMPARE_OP_ALWAYS; + depth_stencil_info.depthBoundsTestEnable = VK_FALSE; + depth_stencil_info.stencilTestEnable = VK_FALSE; + depth_stencil_info.front.failOp = VK_STENCIL_OP_KEEP; + depth_stencil_info.front.passOp = VK_STENCIL_OP_KEEP; + depth_stencil_info.front.depthFailOp = VK_STENCIL_OP_KEEP; + depth_stencil_info.front.compareOp = VK_COMPARE_OP_ALWAYS; + depth_stencil_info.front.compareMask = 0; + depth_stencil_info.front.writeMask = 0; + depth_stencil_info.front.reference = 0; + depth_stencil_info.back.failOp = VK_STENCIL_OP_KEEP; + depth_stencil_info.back.passOp = VK_STENCIL_OP_KEEP; + depth_stencil_info.back.depthFailOp = VK_STENCIL_OP_KEEP; + depth_stencil_info.back.compareOp = VK_COMPARE_OP_ALWAYS; + depth_stencil_info.back.compareMask = 0; + depth_stencil_info.back.writeMask = 0; + depth_stencil_info.back.reference = 0; + depth_stencil_info.minDepthBounds = 0; + depth_stencil_info.maxDepthBounds = 0; + + VkPipelineColorBlendStateCreateInfo blend_info; + blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + blend_info.pNext = nullptr; + blend_info.flags = 0; + blend_info.logicOpEnable = VK_FALSE; + blend_info.logicOp = VK_LOGIC_OP_NO_OP; + + VkPipelineColorBlendAttachmentState blend_attachments[1]; + blend_attachments[0].blendEnable = VK_TRUE; + blend_attachments[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blend_attachments[0].dstColorBlendFactor = + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blend_attachments[0].colorBlendOp = VK_BLEND_OP_ADD; + blend_attachments[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blend_attachments[0].dstAlphaBlendFactor = + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blend_attachments[0].alphaBlendOp = VK_BLEND_OP_ADD; + blend_attachments[0].colorWriteMask = 0xF; + blend_info.attachmentCount = + static_cast(xe::countof(blend_attachments)); + blend_info.pAttachments = blend_attachments; + std::memset(blend_info.blendConstants, 0, sizeof(blend_info.blendConstants)); + + VkPipelineDynamicStateCreateInfo dynamic_state_info; + dynamic_state_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state_info.pNext = nullptr; + dynamic_state_info.flags = 0; + // VkDynamicState dynamic_states[] = { + // VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, + //}; + // dynamic_state_info.dynamicStateCount = + // static_cast(xe::countof(dynamic_states)); + // dynamic_state_info.pDynamicStates = dynamic_states; + dynamic_state_info.dynamicStateCount = 0; + dynamic_state_info.pDynamicStates = nullptr; + + VkGraphicsPipelineCreateInfo pipeline_info; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.pNext = nullptr; + pipeline_info.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT; + pipeline_info.stageCount = pipeline_stage_count; + pipeline_info.pStages = pipeline_stages; + pipeline_info.pVertexInputState = &vertex_state_info; + pipeline_info.pInputAssemblyState = &input_info; + pipeline_info.pTessellationState = nullptr; + pipeline_info.pViewportState = &viewport_state_info; + pipeline_info.pRasterizationState = &rasterization_info; + pipeline_info.pMultisampleState = &multisample_info; + pipeline_info.pDepthStencilState = &depth_stencil_info; + pipeline_info.pColorBlendState = &blend_info; + pipeline_info.pDynamicState = &dynamic_state_info; + pipeline_info.layout = pipeline_layout_; + pipeline_info.renderPass = render_state->render_pass_handle; + pipeline_info.subpass = 0; + pipeline_info.basePipelineHandle = nullptr; + pipeline_info.basePipelineIndex = 0; + + VkPipeline pipeline = nullptr; + auto err = vkCreateGraphicsPipelines(device_, nullptr, 1, &pipeline_info, + nullptr, &pipeline); + CheckResult(err, "vkCreateGraphicsPipelines"); + + // TODO(benvanik): don't leak pipelines >_> + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + return true; } void PipelineCache::ClearCache() { // TODO(benvanik): caching. } +VkShaderModule PipelineCache::GetGeometryShader(PrimitiveType primitive_type) { + switch (primitive_type) { + case PrimitiveType::kLineList: + case PrimitiveType::kLineStrip: + case PrimitiveType::kTriangleList: + case PrimitiveType::kTriangleFan: + case PrimitiveType::kTriangleStrip: + // Supported directly - no need to emulate. + return nullptr; + case PrimitiveType::kPointList: + // TODO(benvanik): point list geometry shader. + return nullptr; + case PrimitiveType::kUnknown0x07: + assert_always("Unknown geometry type"); + return nullptr; + case PrimitiveType::kRectangleList: + // TODO(benvanik): rectangle list geometry shader. + return nullptr; + case PrimitiveType::kLineLoop: + // TODO(benvanik): line loop geometry shader. + return nullptr; + case PrimitiveType::kQuadList: + // TODO(benvanik): quad list geometry shader. + return nullptr; + case PrimitiveType::kQuadStrip: + // TODO(benvanik): quad strip geometry shader. + return nullptr; + default: + assert_unhandled_case(primitive_type); + return nullptr; + } +} + bool PipelineCache::SetShadowRegister(uint32_t* dest, uint32_t register_name) { uint32_t value = register_file_->values[register_name].u32; if (*dest == value) { diff --git a/src/xenia/gpu/vulkan/pipeline_cache.h b/src/xenia/gpu/vulkan/pipeline_cache.h index 74c461504..aad43ca80 100644 --- a/src/xenia/gpu/vulkan/pipeline_cache.h +++ b/src/xenia/gpu/vulkan/pipeline_cache.h @@ -30,7 +30,9 @@ namespace vulkan { // including shaders, various blend/etc options, and input configuration. class PipelineCache { public: - PipelineCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device); + PipelineCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device, + VkDescriptorSetLayout uniform_descriptor_set_layout, + VkDescriptorSetLayout texture_descriptor_set_layout); ~PipelineCache(); // Loads a shader from the cache, possibly translating it. @@ -48,26 +50,36 @@ class PipelineCache { VulkanShader* pixel_shader, PrimitiveType primitive_type); - // Currently configured pipeline layout, if any. - VkPipelineLayout current_pipeline_layout() const { return nullptr; } + // Pipeline layout shared by all pipelines. + VkPipelineLayout pipeline_layout() const { return pipeline_layout_; } // Clears all cached content. void ClearCache(); private: - // TODO(benvanik): geometry shader cache. - // TODO(benvanik): translated shader cache. - // TODO(benvanik): pipeline layouts. - // TODO(benvanik): pipeline cache. + // Gets a geometry shader used to emulate the given primitive type. + // Returns nullptr if the primitive doesn't need to be emulated. + VkShaderModule GetGeometryShader(PrimitiveType primitive_type); RegisterFile* register_file_ = nullptr; VkDevice device_ = nullptr; + // Reusable shader translator. SpirvShaderTranslator shader_translator_; + // Disassembler used to get the SPIRV disasm. Only used in debug. xe::ui::spirv::SpirvDisassembler disassembler_; // All loaded shaders mapped by their guest hash key. std::unordered_map shader_map_; + // Vulkan pipeline cache, which in theory helps us out. + // This can be serialized to disk and reused, if we want. + VkPipelineCache pipeline_cache_ = nullptr; + // Layout used for all pipelines describing our uniforms, textures, and push + // constants. + VkPipelineLayout pipeline_layout_ = nullptr; + + // TODO(benvanik): geometry shader cache. + private: enum class UpdateStatus { kCompatible, diff --git a/src/xenia/gpu/vulkan/render_cache.cc b/src/xenia/gpu/vulkan/render_cache.cc index b477d5633..a9595741f 100644 --- a/src/xenia/gpu/vulkan/render_cache.cc +++ b/src/xenia/gpu/vulkan/render_cache.cc @@ -542,7 +542,9 @@ const RenderState* RenderCache::BeginRenderPass(VkCommandBuffer command_buffer, return nullptr; } current_state_.render_pass = render_pass; + current_state_.render_pass_handle = render_pass->handle; current_state_.framebuffer = framebuffer; + current_state_.framebuffer_handle = framebuffer->handle; } if (!render_pass) { return nullptr; diff --git a/src/xenia/gpu/vulkan/render_cache.h b/src/xenia/gpu/vulkan/render_cache.h index 865b34cfd..4a1574e9b 100644 --- a/src/xenia/gpu/vulkan/render_cache.h +++ b/src/xenia/gpu/vulkan/render_cache.h @@ -69,8 +69,10 @@ struct RenderState { RenderConfiguration config; // Render pass (to be used with pipelines/etc). CachedRenderPass* render_pass = nullptr; + VkRenderPass render_pass_handle = nullptr; // Target framebuffer bound to the render pass. CachedFramebuffer* framebuffer = nullptr; + VkFramebuffer framebuffer_handle = nullptr; }; // Manages the virtualized EDRAM and the render target cache. diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index bf95ef6a4..ea051ca52 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -22,11 +22,57 @@ namespace vulkan { using xe::ui::vulkan::CheckResult; +constexpr uint32_t kMaxTextureSamplers = 32; + TextureCache::TextureCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device) - : register_file_(register_file), device_(*device) {} + : register_file_(register_file), device_(*device) { + // Descriptor pool used for all of our cached descriptors. + VkDescriptorPoolCreateInfo descriptor_pool_info; + descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptor_pool_info.pNext = nullptr; + descriptor_pool_info.flags = + VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + descriptor_pool_info.maxSets = 256; + VkDescriptorPoolSize pool_sizes[1]; + pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_sizes[0].descriptorCount = 256; + descriptor_pool_info.poolSizeCount = 1; + descriptor_pool_info.pPoolSizes = pool_sizes; + auto err = vkCreateDescriptorPool(device_, &descriptor_pool_info, nullptr, + &descriptor_pool_); + CheckResult(err, "vkCreateDescriptorPool"); -TextureCache::~TextureCache() = default; + // Create the descriptor set layout used for rendering. + // We always have the same number of samplers but only some are used. + VkDescriptorSetLayoutBinding texture_bindings[1]; + for (int i = 0; i < 1; ++i) { + auto& texture_binding = texture_bindings[i]; + texture_binding.binding = 0; + texture_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + texture_binding.descriptorCount = kMaxTextureSamplers; + texture_binding.stageFlags = + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + texture_binding.pImmutableSamplers = nullptr; + } + VkDescriptorSetLayoutCreateInfo descriptor_set_layout_info; + descriptor_set_layout_info.sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_set_layout_info.pNext = nullptr; + descriptor_set_layout_info.flags = 0; + descriptor_set_layout_info.bindingCount = + static_cast(xe::countof(texture_bindings)); + descriptor_set_layout_info.pBindings = texture_bindings; + err = vkCreateDescriptorSetLayout(device_, &descriptor_set_layout_info, + nullptr, &texture_descriptor_set_layout_); + CheckResult(err, "vkCreateDescriptorSetLayout"); +} + +TextureCache::~TextureCache() { + vkDestroyDescriptorSetLayout(device_, texture_descriptor_set_layout_, + nullptr); + vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr); +} void TextureCache::ClearCache() { // TODO(benvanik): caching. diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h index 3f18a7be1..34ae6f114 100644 --- a/src/xenia/gpu/vulkan/texture_cache.h +++ b/src/xenia/gpu/vulkan/texture_cache.h @@ -26,6 +26,12 @@ class TextureCache { TextureCache(RegisterFile* register_file, ui::vulkan::VulkanDevice* device); ~TextureCache(); + // Descriptor set layout containing all possible texture bindings. + // The set contains one descriptor for each texture sampler [0-31]. + VkDescriptorSetLayout texture_descriptor_set_layout() const { + return texture_descriptor_set_layout_; + } + // TODO(benvanik): UploadTexture. // TODO(benvanik): Resolve. // TODO(benvanik): ReadTexture. @@ -36,6 +42,9 @@ class TextureCache { private: RegisterFile* register_file_ = nullptr; VkDevice device_ = nullptr; + + VkDescriptorPool descriptor_pool_ = nullptr; + VkDescriptorSetLayout texture_descriptor_set_layout_ = nullptr; }; } // namespace vulkan diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index e19e89c29..31460be79 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -69,9 +69,11 @@ bool VulkanCommandProcessor::SetupContext() { // Initialize the state machine caches. buffer_cache_ = std::make_unique(register_file_, device_, kDefaultBufferCacheCapacity); - pipeline_cache_ = std::make_unique(register_file_, device_); - render_cache_ = std::make_unique(register_file_, device_); texture_cache_ = std::make_unique(register_file_, device_); + pipeline_cache_ = std::make_unique( + register_file_, device_, buffer_cache_->constant_descriptor_set_layout(), + texture_cache_->texture_descriptor_set_layout()); + render_cache_ = std::make_unique(register_file_, device_); return true; } @@ -222,29 +224,12 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, return false; } - // Upload the constants the shaders require. - // These are optional, and if none are defined 0 will be returned. - VkDeviceSize vertex_constant_offset = buffer_cache_->UploadConstantRegisters( - vertex_shader->constant_register_map()); - VkDeviceSize pixel_constant_offset = buffer_cache_->UploadConstantRegisters( - pixel_shader->constant_register_map()); - if (vertex_constant_offset == VK_WHOLE_SIZE || - pixel_constant_offset == VK_WHOLE_SIZE) { - // Shader wants constants but we couldn't upload them. + // Pass registers to the shaders. + if (!PopulateConstants(command_buffer, vertex_shader, pixel_shader)) { render_cache_->EndRenderPass(); return false; } - // Configure constant uniform access to point at our offsets. - auto constant_descriptor_set = buffer_cache_->constant_descriptor_set(); - auto pipeline_layout = pipeline_cache_->current_pipeline_layout(); - uint32_t constant_offsets[2] = {static_cast(vertex_constant_offset), - static_cast(pixel_constant_offset)}; - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - pipeline_layout, 0, 1, &constant_descriptor_set, - static_cast(xe::countof(constant_offsets)), - constant_offsets); - // Upload and bind index buffer data (if we have any). if (!PopulateIndexBuffer(command_buffer, index_buffer_info)) { render_cache_->EndRenderPass(); @@ -263,7 +248,6 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, return false; } -#if 0 // Actually issue the draw. if (!index_buffer_info) { // Auto-indexed draw. @@ -282,7 +266,6 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, vkCmdDrawIndexed(command_buffer, index_count, instance_count, first_index, vertex_offset, first_instance); } -#endif // End the rendering pass. render_cache_->EndRenderPass(); @@ -333,6 +316,34 @@ bool VulkanCommandProcessor::IssueDraw(PrimitiveType primitive_type, return true; } +bool VulkanCommandProcessor::PopulateConstants(VkCommandBuffer command_buffer, + VulkanShader* vertex_shader, + VulkanShader* pixel_shader) { + // Upload the constants the shaders require. + // These are optional, and if none are defined 0 will be returned. + VkDeviceSize vertex_constant_offset = buffer_cache_->UploadConstantRegisters( + vertex_shader->constant_register_map()); + VkDeviceSize pixel_constant_offset = buffer_cache_->UploadConstantRegisters( + pixel_shader->constant_register_map()); + if (vertex_constant_offset == VK_WHOLE_SIZE || + pixel_constant_offset == VK_WHOLE_SIZE) { + // Shader wants constants but we couldn't upload them. + return false; + } + + // Configure constant uniform access to point at our offsets. + auto constant_descriptor_set = buffer_cache_->constant_descriptor_set(); + auto pipeline_layout = pipeline_cache_->pipeline_layout(); + uint32_t constant_offsets[2] = {static_cast(vertex_constant_offset), + static_cast(pixel_constant_offset)}; + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline_layout, 0, 1, &constant_descriptor_set, + static_cast(xe::countof(constant_offsets)), + constant_offsets); + + return true; +} + bool VulkanCommandProcessor::PopulateIndexBuffer( VkCommandBuffer command_buffer, IndexBufferInfo* index_buffer_info) { auto& regs = *register_file_; diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.h b/src/xenia/gpu/vulkan/vulkan_command_processor.h index c350f77b0..179c31a73 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.h +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.h @@ -66,6 +66,9 @@ class VulkanCommandProcessor : public CommandProcessor { bool IssueDraw(PrimitiveType primitive_type, uint32_t index_count, IndexBufferInfo* index_buffer_info) override; + bool PopulateConstants(VkCommandBuffer command_buffer, + VulkanShader* vertex_shader, + VulkanShader* pixel_shader); bool PopulateIndexBuffer(VkCommandBuffer command_buffer, IndexBufferInfo* index_buffer_info); bool PopulateVertexBuffers(VkCommandBuffer command_buffer,