[Vulkan] Texture cache: Maximum dimensions, null images
This commit is contained in:
parent
185c23dd50
commit
b80361ee3c
|
@ -134,6 +134,17 @@ void DeferredCommandBuffer::Execute(VkCommandBuffer command_buffer) {
|
|||
attachments, args.rect_count, rects);
|
||||
} break;
|
||||
|
||||
case Command::kVkClearColorImage: {
|
||||
auto& args = *reinterpret_cast<const ArgsVkClearColorImage*>(stream);
|
||||
dfn.vkCmdClearColorImage(
|
||||
command_buffer, args.image, args.image_layout, &args.color,
|
||||
args.range_count,
|
||||
reinterpret_cast<const VkImageSubresourceRange*>(
|
||||
reinterpret_cast<const uint8_t*>(stream) +
|
||||
xe::align(sizeof(ArgsVkClearColorImage),
|
||||
alignof(VkImageSubresourceRange))));
|
||||
} break;
|
||||
|
||||
case Command::kVkCopyBuffer: {
|
||||
auto& args = *reinterpret_cast<const ArgsVkCopyBuffer*>(stream);
|
||||
dfn.vkCmdCopyBuffer(
|
||||
|
|
|
@ -163,6 +163,30 @@ class DeferredCommandBuffer {
|
|||
std::memcpy(rects_arg, rects, sizeof(VkClearRect) * rect_count);
|
||||
}
|
||||
|
||||
VkImageSubresourceRange* CmdClearColorImageEmplace(
|
||||
VkImage image, VkImageLayout image_layout, const VkClearColorValue* color,
|
||||
uint32_t range_count) {
|
||||
const size_t header_size = xe::align(sizeof(ArgsVkClearColorImage),
|
||||
alignof(VkImageSubresourceRange));
|
||||
uint8_t* args_ptr = reinterpret_cast<uint8_t*>(WriteCommand(
|
||||
Command::kVkClearColorImage,
|
||||
header_size + sizeof(VkImageSubresourceRange) * range_count));
|
||||
auto& args = *reinterpret_cast<ArgsVkClearColorImage*>(args_ptr);
|
||||
args.image = image;
|
||||
args.image_layout = image_layout;
|
||||
args.color = *color;
|
||||
args.range_count = range_count;
|
||||
return reinterpret_cast<VkImageSubresourceRange*>(args_ptr + header_size);
|
||||
}
|
||||
void CmdVkClearColorImage(VkImage image, VkImageLayout image_layout,
|
||||
const VkClearColorValue* color,
|
||||
uint32_t range_count,
|
||||
const VkImageSubresourceRange* ranges) {
|
||||
std::memcpy(
|
||||
CmdClearColorImageEmplace(image, image_layout, color, range_count),
|
||||
ranges, sizeof(VkImageSubresourceRange) * range_count);
|
||||
}
|
||||
|
||||
VkBufferCopy* CmdCopyBufferEmplace(VkBuffer src_buffer, VkBuffer dst_buffer,
|
||||
uint32_t region_count) {
|
||||
const size_t header_size =
|
||||
|
@ -316,6 +340,7 @@ class DeferredCommandBuffer {
|
|||
kVkBindPipeline,
|
||||
kVkBindVertexBuffers,
|
||||
kVkClearAttachments,
|
||||
kVkClearColorImage,
|
||||
kVkCopyBuffer,
|
||||
kVkDispatch,
|
||||
kVkDraw,
|
||||
|
@ -386,6 +411,15 @@ class DeferredCommandBuffer {
|
|||
static_assert(alignof(VkClearRect) <= alignof(uintmax_t));
|
||||
};
|
||||
|
||||
struct ArgsVkClearColorImage {
|
||||
VkImage image;
|
||||
VkImageLayout image_layout;
|
||||
VkClearColorValue color;
|
||||
uint32_t range_count;
|
||||
// Followed by aligned VkImageSubresourceRange[].
|
||||
static_assert(alignof(VkImageSubresourceRange) <= alignof(uintmax_t));
|
||||
};
|
||||
|
||||
struct ArgsVkCopyBuffer {
|
||||
VkBuffer src_buffer;
|
||||
VkBuffer dst_buffer;
|
||||
|
|
|
@ -229,6 +229,15 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO(Triang3l): Actual draw resolution scale.
|
||||
texture_cache_ =
|
||||
VulkanTextureCache::Create(*register_file_, *shared_memory_, 1, 1, *this,
|
||||
guest_shader_pipeline_stages_);
|
||||
if (!texture_cache_) {
|
||||
XELOGE("Failed to initialize the texture cache");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shared memory and EDRAM common bindings.
|
||||
VkDescriptorPoolSize descriptor_pool_sizes[1];
|
||||
descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
|
@ -537,6 +546,8 @@ void VulkanCommandProcessor::ShutdownContext() {
|
|||
dfn.vkDestroyDescriptorPool, device,
|
||||
shared_memory_and_edram_descriptor_pool_);
|
||||
|
||||
texture_cache_.reset();
|
||||
|
||||
pipeline_cache_.reset();
|
||||
|
||||
render_target_cache_.reset();
|
||||
|
@ -1747,6 +1758,8 @@ void VulkanCommandProcessor::CheckSubmissionFenceAndDeviceLoss(
|
|||
|
||||
render_target_cache_->CompletedSubmissionUpdated();
|
||||
|
||||
texture_cache_->CompletedSubmissionUpdated(submission_completed_);
|
||||
|
||||
// Destroy outdated swap objects.
|
||||
while (!swap_framebuffers_outdated_.empty()) {
|
||||
const auto& framebuffer_pair = swap_framebuffers_outdated_.front();
|
||||
|
@ -1829,6 +1842,8 @@ bool VulkanCommandProcessor::BeginSubmission(bool is_guest_command) {
|
|||
current_graphics_descriptor_sets_bound_up_to_date_ = 0;
|
||||
|
||||
primitive_processor_->BeginSubmission();
|
||||
|
||||
texture_cache_->BeginSubmission(GetCurrentSubmission());
|
||||
}
|
||||
|
||||
if (is_opening_frame) {
|
||||
|
@ -1854,6 +1869,8 @@ bool VulkanCommandProcessor::BeginSubmission(bool is_guest_command) {
|
|||
uniform_buffer_pool_->Reclaim(frame_completed_);
|
||||
|
||||
primitive_processor_->BeginFrame();
|
||||
|
||||
texture_cache_->BeginFrame();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2093,6 +2110,8 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
|
|||
uniform_buffer_pool_->ClearCache();
|
||||
transient_descriptor_pool_uniform_buffers_->ClearCache();
|
||||
|
||||
texture_cache_->ClearCache();
|
||||
|
||||
pipeline_cache_->ClearCache();
|
||||
|
||||
render_target_cache_->ClearCache();
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_render_target_cache.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_texture_cache.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/ui/vulkan/transient_descriptor_pool.h"
|
||||
|
@ -82,8 +83,10 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
uint64_t GetCurrentFrame() const { return frame_current_; }
|
||||
uint64_t GetCompletedFrame() const { return frame_completed_; }
|
||||
|
||||
// Submission must be open to insert barriers. Returning true if the barrier
|
||||
// has actually been inserted and not dropped.
|
||||
// Submission must be open to insert barriers. If no pipeline stages access
|
||||
// the resource in a synchronization scope, the stage masks should be 0 (top /
|
||||
// bottom of pipe should be specified only if explicitly needed). Returning
|
||||
// true if the barrier has actually been inserted and not dropped.
|
||||
bool PushBufferMemoryBarrier(
|
||||
VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size,
|
||||
VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask,
|
||||
|
@ -328,9 +331,11 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
|
||||
std::unique_ptr<VulkanPrimitiveProcessor> primitive_processor_;
|
||||
|
||||
std::unique_ptr<VulkanRenderTargetCache> render_target_cache_;
|
||||
|
||||
std::unique_ptr<VulkanPipelineCache> pipeline_cache_;
|
||||
|
||||
std::unique_ptr<VulkanRenderTargetCache> render_target_cache_;
|
||||
std::unique_ptr<VulkanTextureCache> texture_cache_;
|
||||
|
||||
VkDescriptorPool shared_memory_and_edram_descriptor_pool_ = VK_NULL_HANDLE;
|
||||
VkDescriptorSet shared_memory_and_edram_descriptor_set_;
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/gpu/vulkan/vulkan_texture_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace vulkan {
|
||||
|
||||
// Generated with `xb buildshaders`.
|
||||
namespace shaders {
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_128bpb_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_128bpb_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_16bpb_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_16bpb_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_32bpb_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_32bpb_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_64bpb_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_64bpb_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_8bpb_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_8bpb_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_ctx1_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_depth_float_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_depth_float_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_depth_unorm_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_depth_unorm_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_dxn_rg8_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_dxt1_rgba8_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_dxt3_rgba8_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_dxt3a_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_dxt3aas1111_argb4_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_dxt5_rgba8_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_dxt5a_r8_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r10g11b11_rgba16_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r10g11b11_rgba16_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r10g11b11_rgba16_snorm_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r10g11b11_rgba16_snorm_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r11g11b10_rgba16_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r11g11b10_rgba16_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r11g11b10_rgba16_snorm_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r11g11b10_rgba16_snorm_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r16_snorm_float_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r16_snorm_float_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r16_unorm_float_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r16_unorm_float_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r4g4b4a4_a4r4g4b4_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r4g4b4a4_a4r4g4b4_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r5g5b5a1_b5g5r5a1_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r5g5b5a1_b5g5r5a1_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r5g5b6_b5g6r5_swizzle_rbga_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r5g5b6_b5g6r5_swizzle_rbga_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r5g6b5_b5g6r5_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_r5g6b5_b5g6r5_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rg16_snorm_float_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rg16_snorm_float_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rg16_unorm_float_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rg16_unorm_float_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rgba16_snorm_float_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rgba16_snorm_float_scaled_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rgba16_unorm_float_cs.h"
|
||||
#include "xenia/gpu/shaders/bytecode/vulkan_spirv/texture_load_rgba16_unorm_float_scaled_cs.h"
|
||||
} // namespace shaders
|
||||
|
||||
VulkanTextureCache::~VulkanTextureCache() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
|
||||
if (null_image_view_3d_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyImageView(device, null_image_view_3d_, nullptr);
|
||||
}
|
||||
if (null_image_view_cube_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyImageView(device, null_image_view_cube_, nullptr);
|
||||
}
|
||||
if (null_image_view_2d_array_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyImageView(device, null_image_view_2d_array_, nullptr);
|
||||
}
|
||||
if (null_image_3d_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyImage(device, null_image_3d_, nullptr);
|
||||
}
|
||||
if (null_image_2d_array_cube_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyImage(device, null_image_2d_array_cube_, nullptr);
|
||||
}
|
||||
for (VkDeviceMemory null_images_memory : null_images_memory_) {
|
||||
if (null_images_memory != VK_NULL_HANDLE) {
|
||||
dfn.vkFreeMemory(device, null_images_memory, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTextureCache::BeginSubmission(uint64_t new_submission_index) {
|
||||
TextureCache::BeginSubmission(new_submission_index);
|
||||
|
||||
if (!null_images_cleared_) {
|
||||
VkImage null_images[] = {null_image_2d_array_cube_, null_image_3d_};
|
||||
VkImageSubresourceRange null_image_subresource_range(
|
||||
ui::vulkan::util::InitializeSubresourceRange());
|
||||
for (size_t i = 0; i < xe::countof(null_images); ++i) {
|
||||
command_processor_.PushImageMemoryBarrier(
|
||||
null_images[i], null_image_subresource_range, 0,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
}
|
||||
command_processor_.SubmitBarriers(true);
|
||||
DeferredCommandBuffer& command_buffer =
|
||||
command_processor_.deferred_command_buffer();
|
||||
// TODO(Triang3l): Find the return value for invalid texture fetch constants
|
||||
// on the real hardware.
|
||||
VkClearColorValue null_image_clear_color;
|
||||
null_image_clear_color.float32[0] = 0.0f;
|
||||
null_image_clear_color.float32[1] = 0.0f;
|
||||
null_image_clear_color.float32[2] = 0.0f;
|
||||
null_image_clear_color.float32[3] = 0.0f;
|
||||
for (size_t i = 0; i < xe::countof(null_images); ++i) {
|
||||
command_buffer.CmdVkClearColorImage(
|
||||
null_images[i], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
&null_image_clear_color, 1, &null_image_subresource_range);
|
||||
}
|
||||
for (size_t i = 0; i < xe::countof(null_images); ++i) {
|
||||
command_processor_.PushImageMemoryBarrier(
|
||||
null_images[i], null_image_subresource_range,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, guest_shader_pipeline_stages_,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
null_images_cleared_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VulkanTextureCache::GetHostFormatSwizzle(TextureKey key) const {
|
||||
// TODO(Triang3l): Implement GetHostFormatSwizzle.
|
||||
return xenos::XE_GPU_TEXTURE_SWIZZLE_RGBA;
|
||||
}
|
||||
|
||||
uint32_t VulkanTextureCache::GetMaxHostTextureWidthHeight(
|
||||
xenos::DataDimension dimension) const {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
provider.device_properties().limits;
|
||||
switch (dimension) {
|
||||
case xenos::DataDimension::k1D:
|
||||
case xenos::DataDimension::k2DOrStacked:
|
||||
// 1D and 2D are emulated as 2D arrays.
|
||||
return device_limits.maxImageDimension2D;
|
||||
case xenos::DataDimension::k3D:
|
||||
return device_limits.maxImageDimension3D;
|
||||
case xenos::DataDimension::kCube:
|
||||
return device_limits.maxImageDimensionCube;
|
||||
default:
|
||||
assert_unhandled_case(dimension);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VulkanTextureCache::GetMaxHostTextureDepthOrArraySize(
|
||||
xenos::DataDimension dimension) const {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
provider.device_properties().limits;
|
||||
switch (dimension) {
|
||||
case xenos::DataDimension::k1D:
|
||||
case xenos::DataDimension::k2DOrStacked:
|
||||
// 1D and 2D are emulated as 2D arrays.
|
||||
return device_limits.maxImageArrayLayers;
|
||||
case xenos::DataDimension::k3D:
|
||||
return device_limits.maxImageDimension3D;
|
||||
case xenos::DataDimension::kCube:
|
||||
// Not requesting the imageCubeArray feature, and the Xenos doesn't
|
||||
// support cube map arrays.
|
||||
return 6;
|
||||
default:
|
||||
assert_unhandled_case(dimension);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<TextureCache::Texture> VulkanTextureCache::CreateTexture(
|
||||
TextureKey key) {
|
||||
// TODO(Triang3l): Implement CreateTexture.
|
||||
return std::unique_ptr<Texture>(new VulkanTexture(*this, key));
|
||||
}
|
||||
|
||||
bool VulkanTextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
||||
bool load_base,
|
||||
bool load_mips) {
|
||||
// TODO(Triang3l): Implement LoadTextureDataFromResidentMemoryImpl.
|
||||
return true;
|
||||
}
|
||||
|
||||
VulkanTextureCache::VulkanTexture::VulkanTexture(
|
||||
VulkanTextureCache& texture_cache, const TextureKey& key)
|
||||
: Texture(texture_cache, key) {}
|
||||
|
||||
VulkanTextureCache::VulkanTextureCache(
|
||||
const RegisterFile& register_file, VulkanSharedMemory& shared_memory,
|
||||
uint32_t draw_resolution_scale_x, uint32_t draw_resolution_scale_y,
|
||||
VulkanCommandProcessor& command_processor,
|
||||
VkPipelineStageFlags guest_shader_pipeline_stages)
|
||||
: TextureCache(register_file, shared_memory, draw_resolution_scale_x,
|
||||
draw_resolution_scale_y),
|
||||
command_processor_(command_processor),
|
||||
guest_shader_pipeline_stages_(guest_shader_pipeline_stages) {
|
||||
// TODO(Triang3l): Support draw resolution scaling.
|
||||
assert_true(draw_resolution_scale_x == 1 && draw_resolution_scale_y == 1);
|
||||
}
|
||||
|
||||
bool VulkanTextureCache::Initialize() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
|
||||
device_portability_subset_features =
|
||||
provider.device_portability_subset_features();
|
||||
|
||||
// Null images as a replacement for unneeded bindings and for bindings for
|
||||
// which the real image hasn't been created.
|
||||
|
||||
VkImageCreateInfo null_image_create_info;
|
||||
null_image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
null_image_create_info.pNext = nullptr;
|
||||
null_image_create_info.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
null_image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
// Four components to return (0, 0, 0, 0).
|
||||
// TODO(Triang3l): Find the return value for invalid texture fetch constants
|
||||
// on the real hardware.
|
||||
null_image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
null_image_create_info.extent.width = 1;
|
||||
null_image_create_info.extent.height = 1;
|
||||
null_image_create_info.extent.depth = 1;
|
||||
null_image_create_info.mipLevels = 1;
|
||||
null_image_create_info.arrayLayers = 6;
|
||||
null_image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
null_image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
null_image_create_info.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
null_image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
null_image_create_info.queueFamilyIndexCount = 0;
|
||||
null_image_create_info.pQueueFamilyIndices = nullptr;
|
||||
null_image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
if (dfn.vkCreateImage(device, &null_image_create_info, nullptr,
|
||||
&null_image_2d_array_cube_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to create the null 2D array and cube "
|
||||
"image");
|
||||
return false;
|
||||
}
|
||||
|
||||
null_image_create_info.flags &= ~VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
null_image_create_info.imageType = VK_IMAGE_TYPE_3D;
|
||||
null_image_create_info.arrayLayers = 1;
|
||||
if (dfn.vkCreateImage(device, &null_image_create_info, nullptr,
|
||||
&null_image_3d_) != VK_SUCCESS) {
|
||||
XELOGE("VulkanTextureCache: Failed to create the null 3D image");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryRequirements null_image_memory_requirements_2d_array_cube_;
|
||||
dfn.vkGetImageMemoryRequirements(
|
||||
device, null_image_2d_array_cube_,
|
||||
&null_image_memory_requirements_2d_array_cube_);
|
||||
VkMemoryRequirements null_image_memory_requirements_3d_;
|
||||
dfn.vkGetImageMemoryRequirements(device, null_image_3d_,
|
||||
&null_image_memory_requirements_3d_);
|
||||
uint32_t null_image_memory_type_common = ui::vulkan::util::ChooseMemoryType(
|
||||
provider,
|
||||
null_image_memory_requirements_2d_array_cube_.memoryTypeBits &
|
||||
null_image_memory_requirements_3d_.memoryTypeBits,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal);
|
||||
if (null_image_memory_type_common != UINT32_MAX) {
|
||||
// Place both null images in one memory allocation because maximum total
|
||||
// memory allocation count is limited.
|
||||
VkDeviceSize null_image_memory_offset_3d_ =
|
||||
xe::align(null_image_memory_requirements_2d_array_cube_.size,
|
||||
std::max(null_image_memory_requirements_3d_.alignment,
|
||||
VkDeviceSize(1)));
|
||||
VkMemoryAllocateInfo null_image_memory_allocate_info;
|
||||
null_image_memory_allocate_info.sType =
|
||||
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
null_image_memory_allocate_info.pNext = nullptr;
|
||||
null_image_memory_allocate_info.allocationSize =
|
||||
null_image_memory_offset_3d_ + null_image_memory_requirements_3d_.size;
|
||||
null_image_memory_allocate_info.memoryTypeIndex =
|
||||
null_image_memory_type_common;
|
||||
if (dfn.vkAllocateMemory(device, &null_image_memory_allocate_info, nullptr,
|
||||
&null_images_memory_[0]) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to allocate the memory for the null "
|
||||
"images");
|
||||
return false;
|
||||
}
|
||||
if (dfn.vkBindImageMemory(device, null_image_2d_array_cube_,
|
||||
null_images_memory_[0], 0) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to bind the memory to the null 2D array "
|
||||
"and cube image");
|
||||
return false;
|
||||
}
|
||||
if (dfn.vkBindImageMemory(device, null_image_3d_, null_images_memory_[0],
|
||||
null_image_memory_offset_3d_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to bind the memory to the null 3D image");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Place each null image in separate allocations.
|
||||
uint32_t null_image_memory_type_2d_array_cube_ =
|
||||
ui::vulkan::util::ChooseMemoryType(
|
||||
provider,
|
||||
null_image_memory_requirements_2d_array_cube_.memoryTypeBits,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal);
|
||||
uint32_t null_image_memory_type_3d_ = ui::vulkan::util::ChooseMemoryType(
|
||||
provider, null_image_memory_requirements_3d_.memoryTypeBits,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal);
|
||||
if (null_image_memory_type_2d_array_cube_ == UINT32_MAX ||
|
||||
null_image_memory_type_3d_ == UINT32_MAX) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to get the memory types for the null "
|
||||
"images");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo null_image_memory_allocate_info;
|
||||
VkMemoryAllocateInfo* null_image_memory_allocate_info_last =
|
||||
&null_image_memory_allocate_info;
|
||||
null_image_memory_allocate_info.sType =
|
||||
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
null_image_memory_allocate_info.pNext = nullptr;
|
||||
null_image_memory_allocate_info.allocationSize =
|
||||
null_image_memory_requirements_2d_array_cube_.size;
|
||||
null_image_memory_allocate_info.memoryTypeIndex =
|
||||
null_image_memory_type_2d_array_cube_;
|
||||
VkMemoryDedicatedAllocateInfoKHR null_image_memory_dedicated_allocate_info;
|
||||
if (provider.device_extensions().khr_dedicated_allocation) {
|
||||
null_image_memory_allocate_info_last->pNext =
|
||||
&null_image_memory_dedicated_allocate_info;
|
||||
null_image_memory_allocate_info_last =
|
||||
reinterpret_cast<VkMemoryAllocateInfo*>(
|
||||
&null_image_memory_dedicated_allocate_info);
|
||||
null_image_memory_dedicated_allocate_info.sType =
|
||||
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
|
||||
null_image_memory_dedicated_allocate_info.pNext = nullptr;
|
||||
null_image_memory_dedicated_allocate_info.image =
|
||||
null_image_2d_array_cube_;
|
||||
null_image_memory_dedicated_allocate_info.buffer = VK_NULL_HANDLE;
|
||||
}
|
||||
if (dfn.vkAllocateMemory(device, &null_image_memory_allocate_info, nullptr,
|
||||
&null_images_memory_[0]) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to allocate the memory for the null 2D "
|
||||
"array and cube image");
|
||||
return false;
|
||||
}
|
||||
if (dfn.vkBindImageMemory(device, null_image_2d_array_cube_,
|
||||
null_images_memory_[0], 0) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to bind the memory to the null 2D array "
|
||||
"and cube image");
|
||||
return false;
|
||||
}
|
||||
|
||||
null_image_memory_allocate_info.allocationSize =
|
||||
null_image_memory_requirements_3d_.size;
|
||||
null_image_memory_allocate_info.memoryTypeIndex =
|
||||
null_image_memory_type_3d_;
|
||||
null_image_memory_dedicated_allocate_info.image = null_image_3d_;
|
||||
if (dfn.vkAllocateMemory(device, &null_image_memory_allocate_info, nullptr,
|
||||
&null_images_memory_[1]) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to allocate the memory for the null 3D "
|
||||
"image");
|
||||
return false;
|
||||
}
|
||||
if (dfn.vkBindImageMemory(device, null_image_3d_, null_images_memory_[1],
|
||||
0) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanTextureCache: Failed to bind the memory to the null 3D image");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo null_image_view_create_info;
|
||||
null_image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
null_image_view_create_info.pNext = nullptr;
|
||||
null_image_view_create_info.flags = 0;
|
||||
null_image_view_create_info.image = null_image_2d_array_cube_;
|
||||
null_image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
null_image_view_create_info.format = null_image_create_info.format;
|
||||
// TODO(Triang3l): Find the return value for invalid texture fetch constants
|
||||
// on the real hardware.
|
||||
// Micro-optimization if this has any effect on the host GPU at all, use only
|
||||
// constant components instead of the real texels. The image will be cleared
|
||||
// to (0, 0, 0, 0) anyway.
|
||||
VkComponentSwizzle null_image_view_swizzle =
|
||||
(!device_portability_subset_features ||
|
||||
device_portability_subset_features->imageViewFormatSwizzle)
|
||||
? VK_COMPONENT_SWIZZLE_ZERO
|
||||
: VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
null_image_view_create_info.components.r = null_image_view_swizzle;
|
||||
null_image_view_create_info.components.g = null_image_view_swizzle;
|
||||
null_image_view_create_info.components.b = null_image_view_swizzle;
|
||||
null_image_view_create_info.components.a = null_image_view_swizzle;
|
||||
null_image_view_create_info.subresourceRange =
|
||||
ui::vulkan::util::InitializeSubresourceRange(
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, 1);
|
||||
if (dfn.vkCreateImageView(device, &null_image_view_create_info, nullptr,
|
||||
&null_image_view_2d_array_) != VK_SUCCESS) {
|
||||
XELOGE("VulkanTextureCache: Failed to create the null 2D array image view");
|
||||
return false;
|
||||
}
|
||||
null_image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
null_image_view_create_info.subresourceRange.layerCount = 6;
|
||||
if (dfn.vkCreateImageView(device, &null_image_view_create_info, nullptr,
|
||||
&null_image_view_cube_) != VK_SUCCESS) {
|
||||
XELOGE("VulkanTextureCache: Failed to create the null cube image view");
|
||||
return false;
|
||||
}
|
||||
null_image_view_create_info.image = null_image_3d_;
|
||||
null_image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D;
|
||||
null_image_view_create_info.subresourceRange.layerCount = 1;
|
||||
if (dfn.vkCreateImageView(device, &null_image_view_create_info, nullptr,
|
||||
&null_image_view_3d_) != VK_SUCCESS) {
|
||||
XELOGE("VulkanTextureCache: Failed to create the null 3D image view");
|
||||
return false;
|
||||
}
|
||||
|
||||
null_images_cleared_ = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_GPU_VULKAN_VULKAN_TEXTURE_CACHE_H_
|
||||
#define XENIA_GPU_VULKAN_VULKAN_TEXTURE_CACHE_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "xenia/gpu/texture_cache.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace vulkan {
|
||||
|
||||
class VulkanCommandProcessor;
|
||||
|
||||
class VulkanTextureCache final : public TextureCache {
|
||||
public:
|
||||
static std::unique_ptr<VulkanTextureCache> Create(
|
||||
const RegisterFile& register_file, VulkanSharedMemory& shared_memory,
|
||||
uint32_t draw_resolution_scale_x, uint32_t draw_resolution_scale_y,
|
||||
VulkanCommandProcessor& command_processor,
|
||||
VkPipelineStageFlags guest_shader_pipeline_stages) {
|
||||
std::unique_ptr<VulkanTextureCache> texture_cache(new VulkanTextureCache(
|
||||
register_file, shared_memory, draw_resolution_scale_x,
|
||||
draw_resolution_scale_y, command_processor,
|
||||
guest_shader_pipeline_stages));
|
||||
if (!texture_cache->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::move(texture_cache);
|
||||
}
|
||||
|
||||
~VulkanTextureCache();
|
||||
|
||||
void BeginSubmission(uint64_t new_submission_index) override;
|
||||
|
||||
protected:
|
||||
uint32_t GetHostFormatSwizzle(TextureKey key) const override;
|
||||
|
||||
uint32_t GetMaxHostTextureWidthHeight(
|
||||
xenos::DataDimension dimension) const override;
|
||||
uint32_t GetMaxHostTextureDepthOrArraySize(
|
||||
xenos::DataDimension dimension) const override;
|
||||
|
||||
std::unique_ptr<Texture> CreateTexture(TextureKey key) override;
|
||||
|
||||
bool LoadTextureDataFromResidentMemoryImpl(Texture& texture, bool load_base,
|
||||
bool load_mips) override;
|
||||
|
||||
private:
|
||||
class VulkanTexture final : public Texture {
|
||||
public:
|
||||
explicit VulkanTexture(VulkanTextureCache& texture_cache,
|
||||
const TextureKey& key);
|
||||
};
|
||||
|
||||
explicit VulkanTextureCache(
|
||||
const RegisterFile& register_file, VulkanSharedMemory& shared_memory,
|
||||
uint32_t draw_resolution_scale_x, uint32_t draw_resolution_scale_y,
|
||||
VulkanCommandProcessor& command_processor,
|
||||
VkPipelineStageFlags guest_shader_pipeline_stages);
|
||||
|
||||
bool Initialize();
|
||||
|
||||
VulkanCommandProcessor& command_processor_;
|
||||
VkPipelineStageFlags guest_shader_pipeline_stages_;
|
||||
|
||||
// If both images can be placed in the same allocation, it's one allocation,
|
||||
// otherwise it's two separate.
|
||||
std::array<VkDeviceMemory, 2> null_images_memory_{};
|
||||
VkImage null_image_2d_array_cube_ = VK_NULL_HANDLE;
|
||||
VkImage null_image_3d_ = VK_NULL_HANDLE;
|
||||
VkImageView null_image_view_2d_array_ = VK_NULL_HANDLE;
|
||||
VkImageView null_image_view_cube_ = VK_NULL_HANDLE;
|
||||
VkImageView null_image_view_3d_ = VK_NULL_HANDLE;
|
||||
bool null_images_cleared_ = false;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_GPU_VULKAN_VULKAN_TEXTURE_CACHE_H_
|
Loading…
Reference in New Issue