[Vulkan] Texture object and view creation

This commit is contained in:
Triang3l 2022-05-19 21:56:15 +03:00
parent b0e1916f75
commit 08769de68b
4 changed files with 1251 additions and 28 deletions

View File

@ -738,6 +738,10 @@ void VulkanCommandProcessor::WriteRegister(uint32_t index, uint32_t value) {
index <= XE_GPU_REG_SHADER_CONSTANT_FETCH_31_5) {
current_graphics_descriptor_set_values_up_to_date_ &=
~(UINT32_C(1) << SpirvShaderTranslator::kDescriptorSetFetchConstants);
if (texture_cache_) {
texture_cache_->TextureFetchConstantWritten(
(index - XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0) / 6);
}
}
}
@ -1548,13 +1552,6 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
*pixel_shader, normalized_color_mask)
: SpirvShaderTranslator::Modification(0);
// Set up the render targets - this may perform dispatches and draws.
if (!render_target_cache_->Update(is_rasterization_done,
normalized_depth_control,
normalized_color_mask, *vertex_shader)) {
return false;
}
// Translate the shaders.
VulkanShader::VulkanTranslation* vertex_shader_translation =
static_cast<VulkanShader::VulkanTranslation*>(
@ -1566,6 +1563,23 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
pixel_shader_modification.value))
: nullptr;
// Update the textures before other work in the submission because samplers
// depend on this (and in case of sampler overflow in a submission,
// submissions must be split) - may perform dispatches.
uint32_t used_texture_mask =
vertex_shader->GetUsedTextureMaskAfterTranslation() |
(pixel_shader != nullptr
? pixel_shader->GetUsedTextureMaskAfterTranslation()
: 0);
texture_cache_->RequestTextures(used_texture_mask);
// Set up the render targets - this may perform dispatches and draws.
if (!render_target_cache_->Update(is_rasterization_done,
normalized_depth_control,
normalized_color_mask, *vertex_shader)) {
return false;
}
// Update the graphics pipeline, and if the new graphics pipeline has a
// different layout, invalidate incompatible descriptor sets before updating
// current_guest_graphics_pipeline_layout_.
@ -2758,7 +2772,9 @@ bool VulkanCommandProcessor::UpdateBindings(const VulkanShader* vertex_shader,
VkDescriptorImageInfo& descriptor_image_info =
descriptor_write_image_info_.emplace_back();
descriptor_image_info.imageView =
texture_cache_->GetNullImageView(texture_binding.dimension);
texture_cache_->GetActiveBindingOrNullImageView(
texture_binding.fetch_constant, texture_binding.dimension,
bool(texture_binding.is_signed));
descriptor_image_info.imageLayout =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
@ -2782,7 +2798,9 @@ bool VulkanCommandProcessor::UpdateBindings(const VulkanShader* vertex_shader,
VkDescriptorImageInfo& descriptor_image_info =
descriptor_write_image_info_.emplace_back();
descriptor_image_info.imageView =
texture_cache_->GetNullImageView(texture_binding.dimension);
texture_cache_->GetActiveBindingOrNullImageView(
texture_binding.fetch_constant, texture_binding.dimension,
bool(texture_binding.is_signed));
descriptor_image_info.imageLayout =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,10 @@
#include <array>
#include <memory>
#include <unordered_map>
#include <utility>
#include "xenia/base/hash.h"
#include "xenia/gpu/texture_cache.h"
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
#include "xenia/ui/vulkan/vulkan_provider.h"
@ -45,18 +47,19 @@ class VulkanTextureCache final : public TextureCache {
void BeginSubmission(uint64_t new_submission_index) override;
VkImageView GetNullImageView(xenos::FetchOpDimension dimension) const {
switch (dimension) {
case xenos::FetchOpDimension::k3DOrStacked:
return null_image_view_3d_;
case xenos::FetchOpDimension::kCube:
return null_image_view_cube_;
default:
return null_image_view_2d_array_;
}
}
// Must be called within a frame - creates and untiles textures needed by
// shaders, and enqueues transitioning them into the sampled usage. This may
// bind compute pipelines (notifying the command processor about that), and
// also since it may insert deferred barriers, before flushing the barriers
// preceding host GPU work.
void RequestTextures(uint32_t used_texture_mask) override;
VkImageView GetActiveBindingOrNullImageView(uint32_t fetch_constant_index,
xenos::FetchOpDimension dimension,
bool is_signed) const;
protected:
bool IsSignedVersionSeparateForFormat(TextureKey key) const override;
uint32_t GetHostFormatSwizzle(TextureKey key) const override;
uint32_t GetMaxHostTextureWidthHeight(
@ -69,13 +72,187 @@ class VulkanTextureCache final : public TextureCache {
bool LoadTextureDataFromResidentMemoryImpl(Texture& texture, bool load_base,
bool load_mips) override;
void UpdateTextureBindingsImpl(uint32_t fetch_constant_mask) override;
private:
enum class LoadMode {
k8bpb,
k16bpb,
k32bpb,
k64bpb,
k128bpb,
kR5G5B5A1ToB5G5R5A1,
kR5G6B5ToB5G6R5,
kR5G5B6ToB5G6R5WithRBGASwizzle,
kRGBA4ToARGB4,
kR10G11B11ToRGBA16,
kR10G11B11ToRGBA16SNorm,
kR11G11B10ToRGBA16,
kR11G11B10ToRGBA16SNorm,
kR16UNormToFloat,
kR16SNormToFloat,
kRG16UNormToFloat,
kRG16SNormToFloat,
kRGBA16UNormToFloat,
kRGBA16SNormToFloat,
kDXT1ToRGBA8,
kDXT3ToRGBA8,
kDXT5ToRGBA8,
kDXNToRG8,
kDXT3A,
kDXT3AAs1111ToARGB4,
kDXT5AToR8,
kCTX1,
kDepthUnorm,
kDepthFloat,
kCount,
kUnknown = kCount
};
struct HostFormat {
LoadMode load_mode;
// Do NOT add integer formats to this - they are not filterable, can only be
// read with ImageFetch, not ImageSample! If any game is seen using
// num_format 1 for fixed-point formats (for floating-point, it's normally
// set to 1 though), add a constant buffer containing multipliers for the
// textures and multiplication to the tfetch implementation.
VkFormat format;
uint32_t block_width_log2;
uint32_t block_height_log2;
// Set up dynamically based on what's supported by the device.
bool linear_filterable;
};
struct HostFormatPair {
HostFormat format_unsigned;
HostFormat format_signed;
// Mapping of Xenos swizzle components to Vulkan format components.
uint32_t swizzle;
// Whether the unsigned and the signed formats are compatible for one image
// and the same image data (on a portability subset device, this should also
// take imageViewFormatReinterpretation into account).
bool unsigned_signed_compatible;
};
class VulkanTexture final : public Texture {
public:
enum class Usage {
kUndefined,
kTransferDestination,
kGuestShaderSampled,
kSwapSampled,
};
// Takes ownership of the image and its memory.
explicit VulkanTexture(VulkanTextureCache& texture_cache,
const TextureKey& key);
const TextureKey& key, VkImage image,
VkDeviceMemory memory, VkDeviceSize memory_size);
~VulkanTexture();
VkImage image() const { return image_; }
// Doesn't transition (the caller must insert the barrier).
Usage SetUsage(Usage new_usage) {
Usage old_usage = usage_;
usage_ = new_usage;
return old_usage;
}
VkImageView GetView(bool is_signed, uint32_t host_swizzle);
private:
union ViewKey {
uint32_t key;
struct {
uint32_t is_signed_separate_view : 1;
uint32_t host_swizzle : 12;
};
ViewKey() : key(0) { static_assert_size(*this, sizeof(key)); }
struct Hasher {
size_t operator()(const ViewKey& key) const {
return std::hash<decltype(key.key)>{}(key.key);
}
};
bool operator==(const ViewKey& other_key) const {
return key == other_key.key;
}
bool operator!=(const ViewKey& other_key) const {
return !(*this == other_key);
}
};
static constexpr VkComponentSwizzle GetComponentSwizzle(
uint32_t texture_swizzle, uint32_t component_index) {
xenos::XE_GPU_TEXTURE_SWIZZLE texture_component_swizzle =
xenos::XE_GPU_TEXTURE_SWIZZLE(
(texture_swizzle >> (3 * component_index)) & 0b111);
if (texture_component_swizzle ==
xenos::XE_GPU_TEXTURE_SWIZZLE(component_index)) {
// The portability subset requires all swizzles to be IDENTITY, return
// IDENTITY specifically, not R, G, B, A.
return VK_COMPONENT_SWIZZLE_IDENTITY;
}
switch (texture_component_swizzle) {
case xenos::XE_GPU_TEXTURE_SWIZZLE_R:
return VK_COMPONENT_SWIZZLE_R;
case xenos::XE_GPU_TEXTURE_SWIZZLE_G:
return VK_COMPONENT_SWIZZLE_G;
case xenos::XE_GPU_TEXTURE_SWIZZLE_B:
return VK_COMPONENT_SWIZZLE_B;
case xenos::XE_GPU_TEXTURE_SWIZZLE_A:
return VK_COMPONENT_SWIZZLE_A;
case xenos::XE_GPU_TEXTURE_SWIZZLE_0:
return VK_COMPONENT_SWIZZLE_ZERO;
case xenos::XE_GPU_TEXTURE_SWIZZLE_1:
return VK_COMPONENT_SWIZZLE_ONE;
default:
// An invalid value.
return VK_COMPONENT_SWIZZLE_IDENTITY;
}
}
VkImage image_;
VkDeviceMemory memory_;
Usage usage_ = Usage::kUndefined;
std::unordered_map<ViewKey, VkImageView, ViewKey::Hasher> views_;
};
struct VulkanTextureBinding {
VkImageView image_view_unsigned;
VkImageView image_view_signed;
VulkanTextureBinding() { Reset(); }
void Reset() {
image_view_unsigned = VK_NULL_HANDLE;
image_view_signed = VK_NULL_HANDLE;
}
};
static constexpr bool AreDimensionsCompatible(
xenos::FetchOpDimension binding_dimension,
xenos::DataDimension resource_dimension) {
switch (binding_dimension) {
case xenos::FetchOpDimension::k1D:
case xenos::FetchOpDimension::k2D:
return resource_dimension == xenos::DataDimension::k1D ||
resource_dimension == xenos::DataDimension::k2DOrStacked;
case xenos::FetchOpDimension::k3DOrStacked:
return resource_dimension == xenos::DataDimension::k3D;
case xenos::FetchOpDimension::kCube:
return resource_dimension == xenos::DataDimension::kCube;
default:
return false;
}
}
explicit VulkanTextureCache(
const RegisterFile& register_file, VulkanSharedMemory& shared_memory,
uint32_t draw_resolution_scale_x, uint32_t draw_resolution_scale_y,
@ -84,9 +261,16 @@ class VulkanTextureCache final : public TextureCache {
bool Initialize();
void GetTextureUsageMasks(VulkanTexture::Usage usage,
VkPipelineStageFlags& stage_mask,
VkAccessFlags& access_mask, VkImageLayout& layout);
VulkanCommandProcessor& command_processor_;
VkPipelineStageFlags guest_shader_pipeline_stages_;
static const HostFormatPair kBestHostFormats[64];
HostFormatPair host_formats_[64];
// 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_{};
@ -96,6 +280,9 @@ class VulkanTextureCache final : public TextureCache {
VkImageView null_image_view_cube_ = VK_NULL_HANDLE;
VkImageView null_image_view_3d_ = VK_NULL_HANDLE;
bool null_images_cleared_ = false;
std::array<VulkanTextureBinding, xenos::kTextureFetchConstantCount>
vulkan_texture_bindings_;
};
} // namespace vulkan

View File

@ -5,6 +5,7 @@ XE_UI_VULKAN_FUNCTION(vkEnumerateDeviceExtensionProperties)
XE_UI_VULKAN_FUNCTION(vkEnumeratePhysicalDevices)
XE_UI_VULKAN_FUNCTION(vkGetDeviceProcAddr)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceFeatures)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceFormatProperties)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceMemoryProperties)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceProperties)
XE_UI_VULKAN_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties)