diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index 57af04e24..3f991baa8 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -164,33 +164,37 @@ void SpirvShaderTranslator::StartTranslation() { push_constants_type, "push_consts"); // Texture bindings - Id samplers_t = b.makeSamplerType(); - Id img_t[] = { - b.makeImageType(float_type_, spv::Dim::Dim1D, false, false, false, 1, - spv::ImageFormat::ImageFormatUnknown), - b.makeImageType(float_type_, spv::Dim::Dim2D, false, false, false, 1, - spv::ImageFormat::ImageFormatUnknown), - b.makeImageType(float_type_, spv::Dim::Dim3D, false, false, false, 1, - spv::ImageFormat::ImageFormatUnknown), - b.makeImageType(float_type_, spv::Dim::DimCube, false, false, false, 1, - spv::ImageFormat::ImageFormatUnknown)}; + Id tex_t[] = {b.makeSampledImageType(b.makeImageType( + float_type_, spv::Dim::Dim1D, false, false, false, 1, + spv::ImageFormat::ImageFormatUnknown)), + b.makeSampledImageType(b.makeImageType( + float_type_, spv::Dim::Dim2D, false, false, false, 1, + spv::ImageFormat::ImageFormatUnknown)), + b.makeSampledImageType(b.makeImageType( + float_type_, spv::Dim::Dim3D, false, false, false, 1, + spv::ImageFormat::ImageFormatUnknown)), + b.makeSampledImageType(b.makeImageType( + float_type_, spv::Dim::DimCube, false, false, false, 1, + spv::ImageFormat::ImageFormatUnknown))}; - Id samplers_a = b.makeArrayType(samplers_t, b.makeUintConstant(32), 0); - Id img_a_t[] = {b.makeArrayType(img_t[0], b.makeUintConstant(32), 0), - b.makeArrayType(img_t[1], b.makeUintConstant(32), 0), - b.makeArrayType(img_t[2], b.makeUintConstant(32), 0), - b.makeArrayType(img_t[3], b.makeUintConstant(32), 0)}; + // Id samplers_a = b.makeArrayType(sampler_t, b.makeUintConstant(32), 0); + Id tex_a_t[] = {b.makeArrayType(tex_t[0], b.makeUintConstant(32), 0), + b.makeArrayType(tex_t[1], b.makeUintConstant(32), 0), + b.makeArrayType(tex_t[2], b.makeUintConstant(32), 0), + b.makeArrayType(tex_t[3], b.makeUintConstant(32), 0)}; - samplers_ = b.createVariable(spv::StorageClass::StorageClassUniformConstant, - samplers_a, "samplers"); - b.addDecoration(samplers_, spv::Decoration::DecorationDescriptorSet, 1); - b.addDecoration(samplers_, spv::Decoration::DecorationBinding, 0); + // TODO(DrChat): See texture_cache.cc - do we need separate samplers here? + // samplers_ = + // b.createVariable(spv::StorageClass::StorageClassUniformConstant, + // samplers_a, "samplers"); + // b.addDecoration(samplers_, spv::Decoration::DecorationDescriptorSet, 1); + // b.addDecoration(samplers_, spv::Decoration::DecorationBinding, 0); for (int i = 0; i < 4; i++) { - img_[i] = b.createVariable(spv::StorageClass::StorageClassUniformConstant, - img_a_t[i], - xe::format_string("images%dD", i + 1).c_str()); - b.addDecoration(img_[i], spv::Decoration::DecorationDescriptorSet, 1); - b.addDecoration(img_[i], spv::Decoration::DecorationBinding, i + 1); + tex_[i] = b.createVariable(spv::StorageClass::StorageClassUniformConstant, + tex_a_t[i], + xe::format_string("textures%dD", i + 1).c_str()); + b.addDecoration(tex_[i], spv::Decoration::DecorationDescriptorSet, 1); + b.addDecoration(tex_[i], spv::Decoration::DecorationBinding, i + 1); } // Interpolators. @@ -674,25 +678,15 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( switch (instr.opcode) { case FetchOpcode::kTextureFetch: { - auto image_index = b.makeUintConstant(instr.operands[1].storage_index); - auto image_ptr = + auto texture_index = b.makeUintConstant(instr.operands[1].storage_index); + auto texture_ptr = b.createAccessChain(spv::StorageClass::StorageClassUniformConstant, - img_[dim_idx], std::vector({image_index})); - auto sampler_ptr = - b.createAccessChain(spv::StorageClass::StorageClassUniformConstant, - samplers_, std::vector({image_index})); - auto image = b.createLoad(image_ptr); - auto sampler = b.createLoad(sampler_ptr); - assert(b.isImageType(b.getTypeId(image))); - assert(b.isSamplerType(b.getTypeId(sampler))); - - auto sampled_image_type = b.makeSampledImageType(b.getImageType(image)); - auto tex = b.createBinOp(spv::Op::OpSampledImage, sampled_image_type, - image, sampler); + tex_[dim_idx], std::vector({texture_index})); + auto texture = b.createLoad(texture_ptr); spv::Builder::TextureParameters params = {0}; params.coords = src; - params.sampler = tex; + params.sampler = texture; dest = b.createTextureCall(spv::NoPrecision, vec4_float_type_, false, false, false, false, false, params); } break; @@ -1741,10 +1735,18 @@ void SpirvShaderTranslator::StoreToResult(Id source_value_id, auto n_dst = b.getNumTypeComponents(storage_type); assert_true(n_el < n_dst); - constituents.push_back(source_value_id); - for (int i = n_el; i < n_dst; i++) { - // Pad with zeroes. - constituents.push_back(b.makeFloatConstant(0.f)); + if (n_el == 1) { + // Smear scalar. + for (int i = 0; i < n_dst; i++) { + constituents.push_back(source_value_id); + } + } else { + // FIXME: This may not work as intended. + constituents.push_back(source_value_id); + for (int i = n_el; i < n_dst; i++) { + // Pad with zeroes. + constituents.push_back(b.makeFloatConstant(0.f)); + } } source_value_id = diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index ed4356322..3327dccbd 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -122,7 +122,7 @@ class SpirvShaderTranslator : public ShaderTranslator { spv::Id interpolators_ = 0; spv::Id frag_outputs_ = 0; spv::Id samplers_ = 0; - spv::Id img_[4] = {0}; // Images {1D, 2D, 3D, Cube} + spv::Id tex_[4] = {0}; // Images {1D, 2D, 3D, Cube} // Map of {binding -> {offset -> spv input}} std::map> vertex_binding_map_; diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 8a8e2e2f4..5c6e42b8b 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -42,7 +42,7 @@ TextureCache::TextureCache(RegisterFile* register_file, VkDescriptorPoolSize pool_sizes[2]; pool_sizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLER; pool_sizes[0].descriptorCount = 32; - pool_sizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_sizes[1].descriptorCount = 32; descriptor_pool_info.poolSizeCount = 2; descriptor_pool_info.pPoolSizes = pool_sizes; @@ -63,7 +63,7 @@ TextureCache::TextureCache(RegisterFile* register_file, for (int i = 0; i < 4; ++i) { auto& texture_binding = bindings[1 + i]; texture_binding.binding = 1 + i; - texture_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texture_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; texture_binding.descriptorCount = kMaxTextureSamplers; texture_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; @@ -94,35 +94,37 @@ TextureCache::TextureCache(RegisterFile* register_file, err = vkCreateBuffer(*device_, &staging_buffer_info, nullptr, &staging_buffer_); CheckResult(err, "vkCreateBuffer"); - - if (err == VK_SUCCESS) { - VkMemoryRequirements staging_buffer_reqs; - vkGetBufferMemoryRequirements(*device_, staging_buffer_, - &staging_buffer_reqs); - staging_buffer_mem_ = device_->AllocateMemory(staging_buffer_reqs); - assert_not_null(staging_buffer_mem_); - - err = vkBindBufferMemory(*device_, staging_buffer_, staging_buffer_mem_, 0); - CheckResult(err, "vkBindBufferMemory"); - - // Upload a grid into the staging buffer. - uint32_t* gpu_data = nullptr; - err = - vkMapMemory(*device_, staging_buffer_mem_, 0, staging_buffer_info.size, - 0, reinterpret_cast(&gpu_data)); - CheckResult(err, "vkMapMemory"); - - int width = 2048; - int height = 2048; - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - gpu_data[y * width + x] = - ((y % 32 < 16) ^ (x % 32 >= 16)) ? 0xFF0000FF : 0xFFFFFFFF; - } - } - - vkUnmapMemory(*device_, staging_buffer_mem_); + if (err != VK_SUCCESS) { + // This isn't good. + assert_always(); + return; } + + VkMemoryRequirements staging_buffer_reqs; + vkGetBufferMemoryRequirements(*device_, staging_buffer_, + &staging_buffer_reqs); + staging_buffer_mem_ = device_->AllocateMemory(staging_buffer_reqs); + assert_not_null(staging_buffer_mem_); + + err = vkBindBufferMemory(*device_, staging_buffer_, staging_buffer_mem_, 0); + CheckResult(err, "vkBindBufferMemory"); + + // Upload a grid into the staging buffer. + uint32_t* gpu_data = nullptr; + err = vkMapMemory(*device_, staging_buffer_mem_, 0, staging_buffer_info.size, + 0, reinterpret_cast(&gpu_data)); + CheckResult(err, "vkMapMemory"); + + int width = 2048; + int height = 2048; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + gpu_data[y * width + x] = + ((y % 32 < 16) ^ (x % 32 >= 16)) ? 0xFF0000FF : 0xFFFFFFFF; + } + } + + vkUnmapMemory(*device_, staging_buffer_mem_); } TextureCache::~TextureCache() { @@ -131,9 +133,141 @@ TextureCache::~TextureCache() { vkDestroyDescriptorPool(*device_, descriptor_pool_, nullptr); } +TextureCache::Texture* TextureCache::AllocateTexture( + const TextureInfo& texture_info) { + // Create an image first. + VkImageCreateInfo image_info = {}; + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + switch (texture_info.dimension) { + case Dimension::k1D: + image_info.imageType = VK_IMAGE_TYPE_1D; + break; + case Dimension::k2D: + image_info.imageType = VK_IMAGE_TYPE_2D; + break; + case Dimension::k3D: + image_info.imageType = VK_IMAGE_TYPE_3D; + break; + case Dimension::kCube: + image_info.imageType = VK_IMAGE_TYPE_2D; + image_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + break; + default: + assert_unhandled_case(texture_info.dimension); + return nullptr; + } + + // TODO: Format + image_info.format = VK_FORMAT_R8G8B8A8_UNORM; + image_info.extent = {texture_info.width + 1, texture_info.height + 1, + texture_info.depth + 1}; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.queueFamilyIndexCount = 0; + image_info.pQueueFamilyIndices = nullptr; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImage image; + auto err = vkCreateImage(*device_, &image_info, nullptr, &image); + CheckResult(err, "vkCreateImage"); + + VkMemoryRequirements mem_requirements; + vkGetImageMemoryRequirements(*device_, image, &mem_requirements); + + // TODO: Use a circular buffer or something else to allocate this memory. + // The device has a limited amount (around 64) of memory allocations that we + // can make. + // Now that we have the size, back the image with GPU memory. + auto memory = device_->AllocateMemory(mem_requirements, 0); + if (!memory) { + // Crap. + assert_always(); + vkDestroyImage(*device_, image, nullptr); + return nullptr; + } + + err = vkBindImageMemory(*device_, image, memory, 0); + CheckResult(err, "vkBindImageMemory"); + + auto texture = new Texture(); + texture->format = image_info.format; + texture->image = image; + texture->image_layout = image_info.initialLayout; + texture->image_memory = memory; + texture->memory_offset = 0; + texture->memory_size = mem_requirements.size; + texture->texture_info = texture_info; + + // Create a default view, just for kicks. + VkImageViewCreateInfo view_info; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.pNext = nullptr; + view_info.flags = 0; + view_info.image = image; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = image_info.format; + view_info.components = { + VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A, + }; + view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + VkImageView view; + err = vkCreateImageView(*device_, &view_info, nullptr, &view); + CheckResult(err, "vkCreateImageView"); + if (err == VK_SUCCESS) { + auto texture_view = std::make_unique(); + texture_view->texture = texture; + texture_view->view = view; + texture->views.push_back(std::move(texture_view)); + } + + return texture; +} + +bool TextureCache::FreeTexture(Texture* texture) { + // TODO(DrChat) + return false; +} + +TextureCache::Texture* TextureCache::DemandResolveTexture( + const TextureInfo& texture_info, TextureFormat format, + uint32_t* out_offset_x, uint32_t* out_offset_y) { + // Check to see if we've already used a texture at this location. + auto texture = LookupAddress( + texture_info.guest_address, texture_info.size_2d.block_width, + texture_info.size_2d.block_height, format, out_offset_x, out_offset_y); + if (texture) { + return texture; + } + + // Check resolve textures. + for (auto it = resolve_textures_.begin(); it != resolve_textures_.end(); + ++it) { + texture = (*it).get(); + if (texture_info.guest_address == texture->texture_info.guest_address && + texture_info.size_2d.logical_width == + texture->texture_info.size_2d.logical_width && + texture_info.size_2d.logical_height == + texture->texture_info.size_2d.logical_height) { + // Exact match. + return texture; + } + } + + // No texture at this location. Make a new one. + texture = AllocateTexture(texture_info); + resolve_textures_.push_back(std::unique_ptr(texture)); + return texture; +} + TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, VkCommandBuffer command_buffer) { - // Run a tight loop to scan for an existing texture. + // Run a tight loop to scan for an exact match existing texture. auto texture_hash = texture_info.hash(); for (auto it = textures_.find(texture_hash); it != textures_.end(); ++it) { if (it->second->texture_info == texture_info) { @@ -141,15 +275,25 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, } } - // Though we didn't find an exact match, that doesn't mean we're out of the - // woods yet. This texture could either be a portion of another texture or - // vice versa. Check for overlap before uploading. - for (auto it = textures_.begin(); it != textures_.end(); ++it) { + // Check resolve textures. + for (auto it = resolve_textures_.begin(); it != resolve_textures_.end(); + ++it) { + auto texture = (*it).get(); + if (texture_info.guest_address == texture->texture_info.guest_address && + texture_info.size_2d.logical_width == + texture->texture_info.size_2d.logical_width && + texture_info.size_2d.logical_height == + texture->texture_info.size_2d.logical_height) { + // Exact match. + // TODO: Lazy match + texture->texture_info = texture_info; + textures_[texture_hash] = std::move(*it); + } } if (!command_buffer) { - // Texture not found and no command buffer was passed allowing us to upload - // a new one. + // Texture not found and no command buffer was passed, preventing us from + // uploading a new one. return nullptr; } @@ -167,6 +311,12 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, return nullptr; } + // Though we didn't find an exact match, that doesn't mean we're out of the + // woods yet. This texture could either be a portion of another texture or + // vice versa. Copy any overlapping textures into this texture. + for (auto it = textures_.begin(); it != textures_.end(); ++it) { + } + textures_[texture_hash] = std::unique_ptr(texture); return texture; @@ -199,7 +349,7 @@ TextureCache::Sampler* TextureCache::Demand(const SamplerInfo& sampler_info) { sampler_create_info.anisotropyEnable = VK_FALSE; sampler_create_info.maxAnisotropy = 1.0f; sampler_create_info.compareEnable = VK_FALSE; - sampler_create_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_create_info.compareOp = VK_COMPARE_OP_NEVER; sampler_create_info.minLod = 0.0f; sampler_create_info.maxLod = 0.0f; sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; @@ -220,95 +370,21 @@ TextureCache::Sampler* TextureCache::Demand(const SamplerInfo& sampler_info) { return sampler; } -TextureCache::Texture* TextureCache::AllocateTexture(TextureInfo texture_info) { - // Create an image first. - VkImageCreateInfo image_info = {}; - image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - switch (texture_info.dimension) { - case Dimension::k1D: - image_info.imageType = VK_IMAGE_TYPE_1D; - break; - case Dimension::k2D: - image_info.imageType = VK_IMAGE_TYPE_2D; - break; - case Dimension::k3D: - image_info.imageType = VK_IMAGE_TYPE_3D; - break; - case Dimension::kCube: - image_info.imageType = VK_IMAGE_TYPE_2D; - image_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - break; - default: - assert_unhandled_case(texture_info.dimension); - return nullptr; +TextureCache::Texture* TextureCache::LookupAddress( + uint32_t guest_address, uint32_t width, uint32_t height, + TextureFormat format, uint32_t* offset_x, uint32_t* offset_y) { + for (auto it = textures_.begin(); it != textures_.end(); ++it) { + const auto& texture_info = it->second->texture_info; + if (texture_info.guest_address == guest_address && + texture_info.dimension == Dimension::k2D && + texture_info.size_2d.input_width == width && + texture_info.size_2d.input_height == height) { + return it->second.get(); + } } - // TODO: Format - image_info.format = VK_FORMAT_R8G8B8A8_UNORM; - image_info.extent = {texture_info.width + 1, texture_info.height + 1, - texture_info.depth + 1}; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_info.usage = - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.queueFamilyIndexCount = 0; - image_info.pQueueFamilyIndices = nullptr; - image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImage image; - auto err = vkCreateImage(*device_, &image_info, nullptr, &image); - CheckResult(err, "vkCreateImage"); - - VkMemoryRequirements mem_requirements; - vkGetImageMemoryRequirements(*device_, image, &mem_requirements); - - // TODO: Use a circular buffer or something else to allocate this memory. - // The device has a limited amount (around 64) of memory allocations that we - // can make. - // Now that we have the size, back the image with GPU memory. - auto memory = device_->AllocateMemory(mem_requirements, 0); - err = vkBindImageMemory(*device_, image, memory, 0); - CheckResult(err, "vkBindImageMemory"); - - auto texture = new Texture(); - texture->format = image_info.format; - texture->image = image; - texture->memory_offset = 0; - texture->memory_size = mem_requirements.size; - texture->texture_info = texture_info; - texture->texture_memory = memory; - - // Create a default view, just for kicks. - VkImageViewCreateInfo view_info; - view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - view_info.pNext = nullptr; - view_info.flags = 0; - view_info.image = image; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = image_info.format; - view_info.components = { - VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A, - }; - view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - VkImageView view; - err = vkCreateImageView(*device_, &view_info, nullptr, &view); - CheckResult(err, "vkCreateImageView"); - if (err == VK_SUCCESS) { - auto texture_view = std::make_unique(); - texture_view->texture = texture; - texture_view->view = view; - texture->views.push_back(std::move(texture_view)); - } - - return texture; -} - -bool TextureCache::FreeTexture(Texture* texture) { - // TODO(DrChat) - return false; + // TODO: Try to match at an offset. + return nullptr; } bool TextureCache::UploadTexture2D(VkCommandBuffer command_buffer, @@ -359,8 +435,8 @@ bool TextureCache::UploadTexture2D(VkCommandBuffer command_buffer, // For now, just transfer the grid we uploaded earlier into the texture. VkBufferImageCopy copy_region; copy_region.bufferOffset = 0; - copy_region.bufferRowLength = 0; - copy_region.bufferImageHeight = 0; + copy_region.bufferRowLength = 2048; + copy_region.bufferImageHeight = 2048; copy_region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; copy_region.imageOffset = {0, 0, 0}; copy_region.imageExtent = {dest->texture_info.width + 1, @@ -378,6 +454,7 @@ bool TextureCache::UploadTexture2D(VkCommandBuffer command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + dest->image_layout = barrier.newLayout; return true; } @@ -427,6 +504,8 @@ VkDescriptorSet TextureCache::PrepareTextureSet( VkWriteDescriptorSet descriptor_writes[4]; std::memset(descriptor_writes, 0, sizeof(descriptor_writes)); uint32_t descriptor_write_count = 0; + /* + // TODO(DrChat): Do we really need to separate samplers and images here? if (update_set_info->sampler_write_count) { auto& sampler_write = descriptor_writes[descriptor_write_count++]; sampler_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -438,6 +517,7 @@ VkDescriptorSet TextureCache::PrepareTextureSet( sampler_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; sampler_write.pImageInfo = update_set_info->sampler_infos; } + */ if (update_set_info->image_1d_write_count) { auto& image_write = descriptor_writes[descriptor_write_count++]; image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -446,7 +526,7 @@ VkDescriptorSet TextureCache::PrepareTextureSet( image_write.dstBinding = 1; image_write.dstArrayElement = 0; image_write.descriptorCount = update_set_info->image_1d_write_count; - image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + image_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; image_write.pImageInfo = update_set_info->image_1d_infos; } if (update_set_info->image_2d_write_count) { @@ -457,7 +537,7 @@ VkDescriptorSet TextureCache::PrepareTextureSet( image_write.dstBinding = 2; image_write.dstArrayElement = 0; image_write.descriptorCount = update_set_info->image_2d_write_count; - image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + image_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; image_write.pImageInfo = update_set_info->image_2d_infos; } if (update_set_info->image_3d_write_count) { @@ -468,7 +548,7 @@ VkDescriptorSet TextureCache::PrepareTextureSet( image_write.dstBinding = 3; image_write.dstArrayElement = 0; image_write.descriptorCount = update_set_info->image_3d_write_count; - image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + image_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; image_write.pImageInfo = update_set_info->image_3d_infos; } if (update_set_info->image_cube_write_count) { @@ -479,7 +559,7 @@ VkDescriptorSet TextureCache::PrepareTextureSet( image_write.dstBinding = 4; image_write.dstArrayElement = 0; image_write.descriptorCount = update_set_info->image_cube_write_count; - image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + image_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; image_write.pImageInfo = update_set_info->image_cube_infos; } if (descriptor_write_count) { @@ -542,14 +622,11 @@ bool TextureCache::SetupTextureBinding(UpdateSetInfo* update_set_info, trace_writer_->WriteMemoryRead(texture_info.guest_address, texture_info.input_length); - auto& sampler_write = - update_set_info->sampler_infos[update_set_info->sampler_write_count++]; - sampler_write.sampler = sampler->sampler; - auto& image_write = update_set_info->image_2d_infos[update_set_info->image_2d_write_count++]; image_write.imageView = texture->views[0]->view; image_write.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_write.sampler = sampler->sampler; return true; } diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h index 896bb3155..6264a4a98 100644 --- a/src/xenia/gpu/vulkan/texture_cache.h +++ b/src/xenia/gpu/vulkan/texture_cache.h @@ -28,6 +28,30 @@ namespace vulkan { // class TextureCache { public: + struct TextureView; + + // This represents an uploaded Vulkan texture. + struct Texture { + TextureInfo texture_info; + std::vector> views; + + // True if we know all info about this texture, false otherwise. + // (e.g. we resolve to system memory and may not know the full details about + // this texture) + bool full_texture; + VkFormat format; + VkImage image; + VkImageLayout image_layout; + VkDeviceMemory image_memory; + VkDeviceSize memory_offset; + VkDeviceSize memory_size; + }; + + struct TextureView { + Texture* texture; + VkImageView view; + }; + TextureCache(RegisterFile* register_file, TraceWriter* trace_writer, ui::vulkan::VulkanDevice* device); ~TextureCache(); @@ -49,28 +73,24 @@ class TextureCache { // TODO(benvanik): Resolve. // TODO(benvanik): ReadTexture. + // Demands a texture for the purpose of resolving from EDRAM. This either + // creates a new texture or returns a previously created texture. texture_info + // is not required to be completely filled out, just guest_address and size. + // + // It's possible that this may return an image that is larger than the + // requested size (e.g. resolving into a bigger texture) or an image that + // must have an offset applied. If so, the caller must handle this. + // At the very least, it's guaranteed that the image will be large enough to + // hold the requested size. + Texture* DemandResolveTexture(const TextureInfo& texture_info, + TextureFormat format, uint32_t* out_offset_x, + uint32_t* out_offset_y); + // Clears all cached content. void ClearCache(); private: struct UpdateSetInfo; - struct TextureView; - - // This represents an uploaded Vulkan texture. - struct Texture { - TextureInfo texture_info; - VkDeviceMemory texture_memory; - VkDeviceSize memory_offset; - VkDeviceSize memory_size; - VkImage image; - VkFormat format; - std::vector> views; - }; - - struct TextureView { - Texture* texture; - VkImageView view; - }; // Cached Vulkan sampler. struct Sampler { @@ -78,18 +98,28 @@ class TextureCache { VkSampler sampler; }; + // Allocates a new texture and memory to back it on the GPU. + Texture* AllocateTexture(const TextureInfo& texture_info); + bool FreeTexture(Texture* texture); + // Demands a texture. If command_buffer is null and the texture hasn't been // uploaded to graphics memory already, we will return null and bail. Texture* Demand(const TextureInfo& texture_info, VkCommandBuffer command_buffer = nullptr); Sampler* Demand(const SamplerInfo& sampler_info); - // Allocates a new texture and memory to back it on the GPU. - Texture* AllocateTexture(TextureInfo texture_info); - bool FreeTexture(Texture* texture); + // Looks for a texture either containing or matching these parameters. + // Caller is responsible for checking if the texture returned is an exact + // match or just contains the texture given by the parameters. + // If offset_x and offset_y are not null, this may return a texture that + // contains this image at an offset. + Texture* LookupAddress(uint32_t guest_address, uint32_t width, + uint32_t height, TextureFormat format, + uint32_t* offset_x, uint32_t* offset_y); // Queues commands to upload a texture from system memory, applying any - // conversions necessary. + // conversions necessary. This may flush the command buffer to the GPU if we + // run out of staging memory. bool UploadTexture2D(VkCommandBuffer command_buffer, Texture* dest, TextureInfo src); @@ -112,13 +142,12 @@ class TextureCache { VkDeviceMemory staging_buffer_mem_ = nullptr; std::unordered_map> textures_; std::unordered_map> samplers_; + std::vector> resolve_textures_; struct UpdateSetInfo { // Bitmap of all 32 fetch constants and whether they have been setup yet. // This prevents duplication across the vertex and pixel shader. uint32_t has_setup_fetch_mask; - uint32_t sampler_write_count = 0; - VkDescriptorImageInfo sampler_infos[32]; uint32_t image_1d_write_count = 0; VkDescriptorImageInfo image_1d_infos[32]; uint32_t image_2d_write_count = 0;