mirror of
https://github.com/xenia-project/xenia.git
synced 2025-12-06 07:12:03 +01:00
[Vulkan] Refactoring and fixes for VulkanProvider and related areas
Enable portability subset physical device enumeration. Don't use Vulkan 1.1+ logical devices on Vulkan 1.0 instances due to the VkApplicationInfo::apiVersion specification. Make sure all extension dependencies are enabled when creating a device. Prefer exposing feature support over extension support via the device interface to avoid causing confusion with regard to promoted extensions (especially those that required some features as extensions, but had those features made optional when they were promoted). Allow creating presentation-only devices, not demanding any optional features beyond the basic Vulkan 1.0, for use cases such as internal tools or CPU rendering. Require the independentBlend feature for GPU emulation as working around is complicated, while support is almost ubiquitous. Move the graphics system initialization fatal error message to xenia_main after attempting to initialize all implementations, for automatic fallback to other implementations in the future. Log Vulkan driver info. Improve Vulkan debug message logging, enabled by default. Refactor code, with simplified logic for enabling extensions and layers.
This commit is contained in:
parent
a06be03f1b
commit
b5432ab83f
|
|
@ -336,13 +336,42 @@ std::unique_ptr<gpu::GraphicsSystem> EmulatorApp::CreateGraphicsSystem() {
|
|||
// For maintainability, as much implementation code as possible should be
|
||||
// placed in `xe::gpu` and shared between the backends rather than duplicated
|
||||
// between them.
|
||||
const std::string gpu_implementation_name = cvars::gpu;
|
||||
if (gpu_implementation_name == "null") {
|
||||
return std::make_unique<gpu::null::NullGraphicsSystem>();
|
||||
}
|
||||
Factory<gpu::GraphicsSystem> factory;
|
||||
#if XE_PLATFORM_WIN32
|
||||
factory.Add<gpu::d3d12::D3D12GraphicsSystem>("d3d12");
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
factory.Add<gpu::vulkan::VulkanGraphicsSystem>("vulkan");
|
||||
factory.Add<gpu::null::NullGraphicsSystem>("null");
|
||||
return factory.Create(cvars::gpu);
|
||||
std::unique_ptr<gpu::GraphicsSystem> gpu_implementation =
|
||||
factory.Create(gpu_implementation_name);
|
||||
if (!gpu_implementation) {
|
||||
xe::FatalError(
|
||||
"Unable to initialize the graphics subsystem.\n"
|
||||
"\n"
|
||||
#if XE_PLATFORM_ANDROID
|
||||
"The GPU must support at least Vulkan 1.0 with the 'independentBlend' "
|
||||
"feature.\n"
|
||||
"\n"
|
||||
#else
|
||||
#if XE_PLATFORM_WIN32
|
||||
"For Direct3D 12, at least Windows 10 is required, and the GPU must be "
|
||||
"compatible with Direct3D 12 feature level 11_0.\n"
|
||||
"\n"
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
"For Vulkan, the Vulkan runtime must be installed, and the GPU must "
|
||||
"support at least Vulkan 1.0. The Vulkan runtime can be downloaded at "
|
||||
"https://vulkan.lunarg.com/sdk/home.\n"
|
||||
"\n"
|
||||
"Also, ensure that you have the latest driver installed for your GPU.\n"
|
||||
"\n"
|
||||
#endif // XE_PLATFORM_ANDROID
|
||||
"See https://xenia.jp/faq/ for more information and the system "
|
||||
"requirements.");
|
||||
}
|
||||
return gpu_implementation;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<hid::InputDriver>> EmulatorApp::CreateInputDrivers(
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ void ShutdownLogging() {
|
|||
memory::AlignedFree(logger);
|
||||
}
|
||||
|
||||
bool logging::internal::ShouldLog(LogLevel log_level) {
|
||||
bool logging::ShouldLog(LogLevel log_level) {
|
||||
return logger_ != nullptr &&
|
||||
static_cast<int32_t>(log_level) <= cvars::log_level;
|
||||
}
|
||||
|
|
@ -487,7 +487,7 @@ void logging::internal::AppendLogLine(LogLevel log_level,
|
|||
|
||||
void logging::AppendLogLine(LogLevel log_level, const char prefix_char,
|
||||
const std::string_view str) {
|
||||
if (!internal::ShouldLog(log_level) || !str.size()) {
|
||||
if (!ShouldLog(log_level) || !str.size()) {
|
||||
return;
|
||||
}
|
||||
logger_->AppendLine(xe::threading::current_thread_id(), prefix_char,
|
||||
|
|
|
|||
|
|
@ -70,9 +70,16 @@ void InitializeLogging(const std::string_view app_name);
|
|||
void ShutdownLogging();
|
||||
|
||||
namespace logging {
|
||||
namespace internal {
|
||||
|
||||
constexpr char kPrefixCharError = '!';
|
||||
constexpr char kPrefixCharWarning = 'w';
|
||||
constexpr char kPrefixCharInfo = 'i';
|
||||
constexpr char kPrefixCharDebug = 'd';
|
||||
|
||||
bool ShouldLog(LogLevel log_level);
|
||||
|
||||
namespace internal {
|
||||
|
||||
std::pair<char*, size_t> GetThreadBuffer();
|
||||
|
||||
void AppendLogLine(LogLevel log_level, const char prefix_char, size_t written);
|
||||
|
|
@ -83,7 +90,7 @@ void AppendLogLine(LogLevel log_level, const char prefix_char, size_t written);
|
|||
template <typename... Args>
|
||||
void AppendLogLineFormat(LogLevel log_level, const char prefix_char,
|
||||
const char* format, const Args&... args) {
|
||||
if (!internal::ShouldLog(log_level)) {
|
||||
if (!ShouldLog(log_level)) {
|
||||
return;
|
||||
}
|
||||
auto target = internal::GetThreadBuffer();
|
||||
|
|
@ -106,22 +113,26 @@ void FatalError(const std::string_view str);
|
|||
|
||||
template <typename... Args>
|
||||
void XELOGE(const char* format, const Args&... args) {
|
||||
xe::logging::AppendLogLineFormat(xe::LogLevel::Error, '!', format, args...);
|
||||
xe::logging::AppendLogLineFormat(
|
||||
xe::LogLevel::Error, xe::logging::kPrefixCharError, format, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void XELOGW(const char* format, const Args&... args) {
|
||||
xe::logging::AppendLogLineFormat(xe::LogLevel::Warning, 'w', format, args...);
|
||||
xe::logging::AppendLogLineFormat(
|
||||
xe::LogLevel::Warning, xe::logging::kPrefixCharWarning, format, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void XELOGI(const char* format, const Args&... args) {
|
||||
xe::logging::AppendLogLineFormat(xe::LogLevel::Info, 'i', format, args...);
|
||||
xe::logging::AppendLogLineFormat(
|
||||
xe::LogLevel::Info, xe::logging::kPrefixCharInfo, format, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void XELOGD(const char* format, const Args&... args) {
|
||||
xe::logging::AppendLogLineFormat(xe::LogLevel::Debug, 'd', format, args...);
|
||||
xe::logging::AppendLogLineFormat(
|
||||
xe::LogLevel::Debug, xe::logging::kPrefixCharDebug, format, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
|
|
|
|||
|
|
@ -2099,9 +2099,9 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
|||
uint32_t index_count,
|
||||
IndexBufferInfo* index_buffer_info,
|
||||
bool major_mode_explicit) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
ID3D12Device* device = GetD3D12Provider().GetDevice();
|
||||
const RegisterFile& regs = *register_file_;
|
||||
|
|
@ -2594,9 +2594,9 @@ void D3D12CommandProcessor::InitializeTrace() {
|
|||
}
|
||||
|
||||
bool D3D12CommandProcessor::IssueCopy() {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
if (!BeginSubmission(true)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2733,9 +2733,9 @@ void D3D12CommandProcessor::CheckSubmissionFence(uint64_t await_submission) {
|
|||
}
|
||||
|
||||
bool D3D12CommandProcessor::BeginSubmission(bool is_guest_command) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
if (device_removed_) {
|
||||
return false;
|
||||
|
|
@ -3023,9 +3023,9 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(
|
|||
const draw_util::ViewportInfo& viewport_info,
|
||||
const draw_util::Scissor& scissor, bool primitive_polygonal,
|
||||
reg::RB_DEPTHCONTROL normalized_depth_control) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
// Viewport.
|
||||
D3D12_VIEWPORT viewport;
|
||||
|
|
@ -3093,9 +3093,9 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
|
|||
const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask,
|
||||
reg::RB_DEPTHCONTROL normalized_depth_control,
|
||||
uint32_t normalized_color_mask) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
const RegisterFile& regs = *register_file_;
|
||||
auto pa_cl_clip_cntl = regs.Get<reg::PA_CL_CLIP_CNTL>();
|
||||
|
|
@ -3595,9 +3595,9 @@ bool D3D12CommandProcessor::UpdateBindings(const D3D12Shader* vertex_shader,
|
|||
ID3D12Device* device = provider.GetDevice();
|
||||
const RegisterFile& regs = *register_file_;
|
||||
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
// Set the new root signature.
|
||||
if (current_graphics_root_signature_ != root_signature) {
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@ std::string D3D12GraphicsSystem::name() const {
|
|||
X_STATUS D3D12GraphicsSystem::Setup(cpu::Processor* processor,
|
||||
kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
bool is_surface_required) {
|
||||
bool with_presentation) {
|
||||
provider_ = xe::ui::d3d12::D3D12Provider::Create();
|
||||
return GraphicsSystem::Setup(processor, kernel_state, app_context,
|
||||
is_surface_required);
|
||||
with_presentation);
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandProcessor>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class D3D12GraphicsSystem : public GraphicsSystem {
|
|||
|
||||
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
bool is_surface_required) override;
|
||||
bool with_presentation) override;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_shared_memory.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/texture_info.h"
|
||||
#include "xenia/gpu/texture_util.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
|
@ -766,9 +767,9 @@ void D3D12TextureCache::EndFrame() {
|
|||
}
|
||||
|
||||
void D3D12TextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
TextureCache::RequestTextures(used_texture_mask);
|
||||
|
||||
|
|
@ -911,12 +912,12 @@ void D3D12TextureCache::WriteActiveTextureBindfulSRV(
|
|||
}
|
||||
auto device = provider.GetDevice();
|
||||
{
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_i(
|
||||
"gpu",
|
||||
"xe::gpu::d3d12::D3D12TextureCache::WriteActiveTextureBindfulSRV->"
|
||||
"CopyDescriptorsSimple");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
device->CopyDescriptorsSimple(1, handle, source_handle,
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
|
@ -28,9 +29,9 @@ void DeferredCommandList::Reset() { command_stream_.clear(); }
|
|||
|
||||
void DeferredCommandList::Execute(ID3D12GraphicsCommandList* command_list,
|
||||
ID3D12GraphicsCommandList1* command_list_1) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
const uintmax_t* stream = command_stream_.data();
|
||||
size_t stream_remaining = command_stream_.size();
|
||||
ID3D12PipelineState* current_pipeline_state = nullptr;
|
||||
|
|
|
|||
|
|
@ -965,9 +965,9 @@ bool PipelineCache::ConfigurePipeline(
|
|||
uint32_t bound_depth_and_color_render_target_bits,
|
||||
const uint32_t* bound_depth_and_color_render_target_formats,
|
||||
void** pipeline_handle_out, ID3D12RootSignature** root_signature_out) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
assert_not_null(pipeline_handle_out);
|
||||
assert_not_null(root_signature_out);
|
||||
|
|
|
|||
|
|
@ -26,4 +26,6 @@ DECLARE_bool(half_pixel_offset);
|
|||
|
||||
DECLARE_int32(query_occlusion_fake_sample_count);
|
||||
|
||||
#define XE_GPU_FINE_GRAINED_DRAW_SCOPES 1
|
||||
|
||||
#endif // XENIA_GPU_GPU_FLAGS_H_
|
||||
|
|
|
|||
|
|
@ -55,13 +55,13 @@ GraphicsSystem::~GraphicsSystem() = default;
|
|||
X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||
kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
[[maybe_unused]] bool is_surface_required) {
|
||||
bool with_presentation) {
|
||||
memory_ = processor->memory();
|
||||
processor_ = processor;
|
||||
kernel_state_ = kernel_state;
|
||||
app_context_ = app_context;
|
||||
|
||||
if (provider_) {
|
||||
if (with_presentation && provider_) {
|
||||
// Safe if either the UI thread call or the presenter creation fails.
|
||||
if (app_context_) {
|
||||
app_context_->CallInUIThreadSynchronous([this]() {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class GraphicsSystem {
|
|||
virtual X_STATUS Setup(cpu::Processor* processor,
|
||||
kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
bool is_surface_required);
|
||||
bool with_presentation);
|
||||
virtual void Shutdown();
|
||||
|
||||
// May be called from any thread any number of times, even during recovery
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ NullGraphicsSystem::~NullGraphicsSystem() {}
|
|||
X_STATUS NullGraphicsSystem::Setup(cpu::Processor* processor,
|
||||
kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
bool is_surface_required) {
|
||||
bool with_presentation) {
|
||||
// This is a null graphics system, but we still setup vulkan because UI needs
|
||||
// it through us :|
|
||||
provider_ = xe::ui::vulkan::VulkanProvider::Create(is_surface_required);
|
||||
provider_ = xe::ui::vulkan::VulkanProvider::Create(false, with_presentation);
|
||||
return GraphicsSystem::Setup(processor, kernel_state, app_context,
|
||||
is_surface_required);
|
||||
with_presentation);
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandProcessor> NullGraphicsSystem::CreateCommandProcessor() {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class NullGraphicsSystem : public GraphicsSystem {
|
|||
|
||||
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
bool is_surface_required) override;
|
||||
bool with_presentation) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
|
||||
|
|
|
|||
|
|
@ -43,27 +43,34 @@ SpirvShaderTranslator::Features::Features(bool all)
|
|||
demote_to_helper_invocation(all) {}
|
||||
|
||||
SpirvShaderTranslator::Features::Features(
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info)
|
||||
: max_storage_buffer_range(device_info.maxStorageBufferRange),
|
||||
full_draw_index_uint32(device_info.fullDrawIndexUint32),
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device)
|
||||
: max_storage_buffer_range(
|
||||
vulkan_device->properties().maxStorageBufferRange),
|
||||
full_draw_index_uint32(vulkan_device->properties().fullDrawIndexUint32),
|
||||
vertex_pipeline_stores_and_atomics(
|
||||
device_info.vertexPipelineStoresAndAtomics),
|
||||
fragment_stores_and_atomics(device_info.fragmentStoresAndAtomics),
|
||||
clip_distance(device_info.shaderClipDistance),
|
||||
cull_distance(device_info.shaderCullDistance),
|
||||
image_view_format_swizzle(device_info.imageViewFormatSwizzle),
|
||||
vulkan_device->properties().vertexPipelineStoresAndAtomics),
|
||||
fragment_stores_and_atomics(
|
||||
vulkan_device->properties().fragmentStoresAndAtomics),
|
||||
clip_distance(vulkan_device->properties().shaderClipDistance),
|
||||
cull_distance(vulkan_device->properties().shaderCullDistance),
|
||||
image_view_format_swizzle(
|
||||
vulkan_device->properties().imageViewFormatSwizzle),
|
||||
signed_zero_inf_nan_preserve_float32(
|
||||
device_info.shaderSignedZeroInfNanPreserveFloat32),
|
||||
denorm_flush_to_zero_float32(device_info.shaderDenormFlushToZeroFloat32),
|
||||
rounding_mode_rte_float32(device_info.shaderRoundingModeRTEFloat32),
|
||||
vulkan_device->properties().shaderSignedZeroInfNanPreserveFloat32),
|
||||
denorm_flush_to_zero_float32(
|
||||
vulkan_device->properties().shaderDenormFlushToZeroFloat32),
|
||||
rounding_mode_rte_float32(
|
||||
vulkan_device->properties().shaderRoundingModeRTEFloat32),
|
||||
fragment_shader_sample_interlock(
|
||||
device_info.fragmentShaderSampleInterlock),
|
||||
demote_to_helper_invocation(device_info.shaderDemoteToHelperInvocation) {
|
||||
if (device_info.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) {
|
||||
vulkan_device->properties().fragmentShaderSampleInterlock),
|
||||
demote_to_helper_invocation(
|
||||
vulkan_device->properties().shaderDemoteToHelperInvocation) {
|
||||
const uint32_t vulkan_api_version = vulkan_device->properties().apiVersion;
|
||||
if (vulkan_api_version >= VK_MAKE_API_VERSION(0, 1, 2, 0)) {
|
||||
spirv_version = spv::Spv_1_5;
|
||||
} else if (device_info.ext_1_2_VK_KHR_spirv_1_4) {
|
||||
} else if (vulkan_device->extensions().ext_1_2_KHR_spirv_1_4) {
|
||||
spirv_version = spv::Spv_1_4;
|
||||
} else if (device_info.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
} else if (vulkan_api_version >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
spirv_version = spv::Spv_1_3;
|
||||
} else {
|
||||
spirv_version = spv::Spv_1_0;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/gpu/spirv_builder.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
|
@ -320,8 +320,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
static constexpr uint32_t kSpirvMagicToolId = 26;
|
||||
|
||||
struct Features {
|
||||
explicit Features(
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info);
|
||||
explicit Features(const ui::vulkan::VulkanDevice* vulkan_device);
|
||||
explicit Features(bool all = false);
|
||||
|
||||
unsigned int spirv_version;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
@ -31,12 +32,12 @@ DeferredCommandBuffer::DeferredCommandBuffer(
|
|||
void DeferredCommandBuffer::Reset() { command_stream_.clear(); }
|
||||
|
||||
void DeferredCommandBuffer::Execute(VkCommandBuffer command_buffer) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn =
|
||||
command_processor_.GetVulkanProvider().dfn();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn =
|
||||
command_processor_.GetVulkanDevice()->functions();
|
||||
const uintmax_t* stream = command_stream_.data();
|
||||
size_t stream_remaining = command_stream_.size();
|
||||
while (stream_remaining) {
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_api.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/vulkan_presenter.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
@ -72,18 +71,21 @@ VulkanCommandProcessor::VulkanCommandProcessor(
|
|||
: CommandProcessor(graphics_system, kernel_state),
|
||||
deferred_command_buffer_(*this),
|
||||
transient_descriptor_allocator_uniform_buffer_(
|
||||
*static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system->provider()),
|
||||
static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system->provider())
|
||||
->vulkan_device(),
|
||||
&kDescriptorPoolSizeUniformBuffer, 1,
|
||||
kLinkedTypeDescriptorPoolSetCount),
|
||||
transient_descriptor_allocator_storage_buffer_(
|
||||
*static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system->provider()),
|
||||
static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system->provider())
|
||||
->vulkan_device(),
|
||||
&kDescriptorPoolSizeStorageBuffer, 1,
|
||||
kLinkedTypeDescriptorPoolSetCount),
|
||||
transient_descriptor_allocator_textures_(
|
||||
*static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system->provider()),
|
||||
static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system->provider())
|
||||
->vulkan_device(),
|
||||
kDescriptorPoolSizeTextures,
|
||||
uint32_t(xe::countof(kDescriptorPoolSizeTextures)),
|
||||
kLinkedTypeDescriptorPoolSetCount) {}
|
||||
|
|
@ -135,11 +137,11 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
return false;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
vulkan_device->properties();
|
||||
|
||||
// The unconditional inclusion of the vertex shader stage also covers the case
|
||||
// of manual index / factor buffer fetch (the system constants and the shared
|
||||
|
|
@ -148,12 +150,12 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
guest_shader_pipeline_stages_ = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
guest_shader_vertex_stages_ = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
if (device_info.tessellationShader) {
|
||||
if (device_properties.tessellationShader) {
|
||||
guest_shader_pipeline_stages_ |=
|
||||
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
|
||||
guest_shader_vertex_stages_ |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||
}
|
||||
if (!device_info.vertexPipelineStoresAndAtomics) {
|
||||
if (!device_properties.vertexPipelineStoresAndAtomics) {
|
||||
// For memory export from vertex shaders converted to compute shaders.
|
||||
guest_shader_pipeline_stages_ |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||
guest_shader_vertex_stages_ |= VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
|
|
@ -162,10 +164,10 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
// 16384 is bigger than any single uniform buffer that Xenia needs, but is the
|
||||
// minimum maxUniformBufferRange, thus the safe minimum amount.
|
||||
uniform_buffer_pool_ = std::make_unique<ui::vulkan::VulkanUploadBufferPool>(
|
||||
provider, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
vulkan_device, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
xe::align(std::max(ui::GraphicsUploadBufferPool::kDefaultPageSize,
|
||||
size_t(16384)),
|
||||
size_t(device_info.minUniformBufferOffsetAlignment)));
|
||||
size_t(device_properties.minUniformBufferOffsetAlignment)));
|
||||
|
||||
// Descriptor set layouts that don't depend on the setup of other subsystems.
|
||||
VkShaderStageFlags guest_shader_stages =
|
||||
|
|
@ -199,9 +201,10 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
[SpirvShaderTranslator::kConstantBufferSystem]
|
||||
.stageFlags =
|
||||
guest_shader_stages |
|
||||
(device_info.tessellationShader ? VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT
|
||||
(device_properties.tessellationShader
|
||||
? VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT
|
||||
: 0) |
|
||||
(device_info.geometryShader ? VK_SHADER_STAGE_GEOMETRY_BIT : 0);
|
||||
(device_properties.geometryShader ? VK_SHADER_STAGE_GEOMETRY_BIT : 0);
|
||||
descriptor_set_layout_bindings_constants
|
||||
[SpirvShaderTranslator::kConstantBufferFloatVertex]
|
||||
.stageFlags = guest_shader_vertex_stages_;
|
||||
|
|
@ -280,7 +283,7 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
|
||||
uint32_t shared_memory_binding_count_log2 =
|
||||
SpirvShaderTranslator::GetSharedMemoryStorageBufferCountLog2(
|
||||
device_info.maxStorageBufferRange);
|
||||
device_properties.maxStorageBufferRange);
|
||||
uint32_t shared_memory_binding_count = UINT32_C(1)
|
||||
<< shared_memory_binding_count_log2;
|
||||
|
||||
|
|
@ -484,14 +487,14 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
&gamma_ramp_host_visible_buffer_memory_requirements);
|
||||
uint32_t gamma_ramp_host_visible_buffer_memory_types =
|
||||
gamma_ramp_host_visible_buffer_memory_requirements.memoryTypeBits &
|
||||
(device_info.memory_types_device_local &
|
||||
device_info.memory_types_host_visible);
|
||||
(vulkan_device->memory_types().device_local &
|
||||
vulkan_device->memory_types().host_visible);
|
||||
VkMemoryAllocateInfo gamma_ramp_host_visible_buffer_memory_allocate_info;
|
||||
// Prefer a host-uncached (because it's write-only) memory type, but try a
|
||||
// host-cached host-visible device-local one as well.
|
||||
if (xe::bit_scan_forward(
|
||||
gamma_ramp_host_visible_buffer_memory_types &
|
||||
~device_info.memory_types_host_cached,
|
||||
~vulkan_device->memory_types().host_cached,
|
||||
&(gamma_ramp_host_visible_buffer_memory_allocate_info
|
||||
.memoryTypeIndex)) ||
|
||||
xe::bit_scan_forward(
|
||||
|
|
@ -508,7 +511,7 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
gamma_ramp_host_visible_buffer_memory_requirements.size;
|
||||
VkMemoryDedicatedAllocateInfo
|
||||
gamma_ramp_host_visible_buffer_memory_dedicated_allocate_info;
|
||||
if (device_info.ext_1_1_VK_KHR_dedicated_allocation) {
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_dedicated_allocation) {
|
||||
gamma_ramp_host_visible_buffer_memory_allocate_info_last->pNext =
|
||||
&gamma_ramp_host_visible_buffer_memory_dedicated_allocate_info;
|
||||
gamma_ramp_host_visible_buffer_memory_allocate_info_last =
|
||||
|
|
@ -555,7 +558,7 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
if (gamma_ramp_buffer_ == VK_NULL_HANDLE) {
|
||||
// Create separate buffers for the shader and uploading.
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider, kGammaRampSize,
|
||||
vulkan_device, kGammaRampSize,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal, gamma_ramp_buffer_,
|
||||
|
|
@ -564,7 +567,7 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
return false;
|
||||
}
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider, kGammaRampSize * kMaxFramesInFlight,
|
||||
vulkan_device, kGammaRampSize * kMaxFramesInFlight,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
ui::vulkan::util::MemoryPurpose::kUpload, gamma_ramp_upload_buffer_,
|
||||
gamma_ramp_upload_buffer_memory_, &gamma_ramp_upload_memory_type_,
|
||||
|
|
@ -854,11 +857,11 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
bool swap_apply_gamma_pixel_shaders_created =
|
||||
(swap_apply_gamma_pixel_shaders[kSwapApplyGammaPixelShader256EntryTable] =
|
||||
ui::vulkan::util::CreateShaderModule(
|
||||
provider, shaders::apply_gamma_table_ps,
|
||||
vulkan_device, shaders::apply_gamma_table_ps,
|
||||
sizeof(shaders::apply_gamma_table_ps))) != VK_NULL_HANDLE &&
|
||||
(swap_apply_gamma_pixel_shaders[kSwapApplyGammaPixelShaderPWL] =
|
||||
ui::vulkan::util::CreateShaderModule(
|
||||
provider, shaders::apply_gamma_pwl_ps,
|
||||
vulkan_device, shaders::apply_gamma_pwl_ps,
|
||||
sizeof(shaders::apply_gamma_pwl_ps))) != VK_NULL_HANDLE;
|
||||
if (!swap_apply_gamma_pixel_shaders_created) {
|
||||
XELOGE("Failed to create the gamma ramp application pixel shader modules");
|
||||
|
|
@ -879,7 +882,8 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
swap_apply_gamma_pipeline_stages[0].flags = 0;
|
||||
swap_apply_gamma_pipeline_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
swap_apply_gamma_pipeline_stages[0].module =
|
||||
ui::vulkan::util::CreateShaderModule(provider, shaders::fullscreen_cw_vs,
|
||||
ui::vulkan::util::CreateShaderModule(vulkan_device,
|
||||
shaders::fullscreen_cw_vs,
|
||||
sizeof(shaders::fullscreen_cw_vs));
|
||||
if (swap_apply_gamma_pipeline_stages[0].module == VK_NULL_HANDLE) {
|
||||
XELOGE("Failed to create the gamma ramp application vertex shader module");
|
||||
|
|
@ -1037,9 +1041,9 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
void VulkanCommandProcessor::ShutdownContext() {
|
||||
AwaitAllQueueOperationsCompletion();
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
DestroyScratchBuffer();
|
||||
|
||||
|
|
@ -1289,9 +1293,10 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
|||
context);
|
||||
uint64_t guest_output_image_version = vulkan_context.image_version();
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn =
|
||||
vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
uint32_t swap_frame_index =
|
||||
uint32_t(frame_current_ % kMaxFramesInFlight);
|
||||
|
|
@ -1358,7 +1363,7 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
|||
bool gamma_ramp_has_upload_buffer =
|
||||
gamma_ramp_upload_buffer_memory_ != VK_NULL_HANDLE;
|
||||
ui::vulkan::util::FlushMappedMemoryRange(
|
||||
provider,
|
||||
vulkan_device,
|
||||
gamma_ramp_has_upload_buffer ? gamma_ramp_upload_buffer_memory_
|
||||
: gamma_ramp_buffer_memory_,
|
||||
gamma_ramp_upload_memory_type_, gamma_ramp_upload_offset,
|
||||
|
|
@ -1816,9 +1821,9 @@ VkDescriptorSet VulkanCommandProcessor::AllocateSingleTransientDescriptor(
|
|||
descriptor_set = transient_descriptors_free.back();
|
||||
transient_descriptors_free.pop_back();
|
||||
} else {
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
bool is_storage_buffer =
|
||||
transient_descriptor_layout ==
|
||||
SingleTransientDescriptorLayout::kStorageBufferCompute;
|
||||
|
|
@ -1863,9 +1868,9 @@ VkDescriptorSetLayout VulkanCommandProcessor::GetTextureDescriptorSetLayout(
|
|||
return it_existing->second;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
descriptor_set_layout_bindings_.clear();
|
||||
descriptor_set_layout_bindings_.reserve(binding_count);
|
||||
|
|
@ -1959,9 +1964,9 @@ VulkanCommandProcessor::GetPipelineLayout(size_t texture_count_pixel,
|
|||
descriptor_set_layouts[SpirvShaderTranslator::kDescriptorSetTexturesPixel] =
|
||||
descriptor_set_layout_textures_pixel;
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_create_info;
|
||||
pipeline_layout_create_info.sType =
|
||||
|
|
@ -2019,14 +2024,14 @@ VulkanCommandProcessor::AcquireScratchGpuBuffer(
|
|||
|
||||
size = xe::align(size, kScratchBufferSizeIncrement);
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
|
||||
VkDeviceMemory new_scratch_buffer_memory;
|
||||
VkBuffer new_scratch_buffer;
|
||||
// VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT for
|
||||
// texture loading.
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider, size,
|
||||
vulkan_device, size,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal, new_scratch_buffer,
|
||||
new_scratch_buffer_memory)) {
|
||||
|
|
@ -2037,8 +2042,8 @@ VulkanCommandProcessor::AcquireScratchGpuBuffer(
|
|||
}
|
||||
|
||||
if (submission_completed_ >= scratch_buffer_last_usage_submission_) {
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
if (scratch_buffer_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyBuffer(device, scratch_buffer_, nullptr);
|
||||
}
|
||||
|
|
@ -2153,9 +2158,9 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
uint32_t index_count,
|
||||
IndexBufferInfo* index_buffer_info,
|
||||
bool major_mode_explicit) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
const RegisterFile& regs = *register_file_;
|
||||
|
||||
|
|
@ -2165,8 +2170,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
return IssueCopy();
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
GetVulkanProvider().device_info();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
GetVulkanDevice()->properties();
|
||||
|
||||
memexport_ranges_.clear();
|
||||
|
||||
|
|
@ -2182,7 +2187,7 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
// to a compute shader and dispatch it after the draw if the draw doesn't use
|
||||
// tessellation.
|
||||
if (vertex_shader->memexport_eM_written() != 0 &&
|
||||
device_info.vertexPipelineStoresAndAtomics) {
|
||||
device_properties.vertexPipelineStoresAndAtomics) {
|
||||
draw_util::AddMemExportRanges(regs, *vertex_shader, memexport_ranges_);
|
||||
}
|
||||
|
||||
|
|
@ -2213,7 +2218,7 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
}
|
||||
}
|
||||
if (pixel_shader && pixel_shader->memexport_eM_written() != 0 &&
|
||||
device_info.fragmentStoresAndAtomics) {
|
||||
device_properties.fragmentStoresAndAtomics) {
|
||||
draw_util::AddMemExportRanges(regs, *pixel_shader, memexport_ranges_);
|
||||
}
|
||||
|
||||
|
|
@ -2453,9 +2458,9 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
// interlocks case completely - apply the viewport and the scissor offset
|
||||
// directly to pixel address and to things like ps_param_gen.
|
||||
draw_util::GetHostViewportInfo(
|
||||
regs, 1, 1, false, device_info.maxViewportDimensions[0],
|
||||
device_info.maxViewportDimensions[1], true, normalized_depth_control,
|
||||
false, host_render_targets_used,
|
||||
regs, 1, 1, false, device_properties.maxViewportDimensions[0],
|
||||
device_properties.maxViewportDimensions[1], true,
|
||||
normalized_depth_control, false, host_render_targets_used,
|
||||
pixel_shader && pixel_shader->writes_depth(), viewport_info);
|
||||
|
||||
// Update dynamic graphics pipeline state.
|
||||
|
|
@ -2468,7 +2473,7 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
// indirectly in the vertex shader if full 32-bit indices are not supported by
|
||||
// the host.
|
||||
bool shader_32bit_index_dma =
|
||||
!device_info.fullDrawIndexUint32 &&
|
||||
!device_properties.fullDrawIndexUint32 &&
|
||||
primitive_processing_result.index_buffer_type ==
|
||||
PrimitiveProcessor::ProcessedIndexBufferType::kGuestDMA &&
|
||||
vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32 &&
|
||||
|
|
@ -2619,9 +2624,9 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
|||
}
|
||||
|
||||
bool VulkanCommandProcessor::IssueCopy() {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
if (!BeginSubmission(true)) {
|
||||
return false;
|
||||
|
|
@ -2672,9 +2677,9 @@ void VulkanCommandProcessor::CheckSubmissionFenceAndDeviceLoss(
|
|||
await_submission = GetCurrentSubmission() - 1;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
size_t fences_total = submissions_in_flight_fences_.size();
|
||||
size_t fences_awaited = 0;
|
||||
|
|
@ -2787,9 +2792,9 @@ void VulkanCommandProcessor::CheckSubmissionFenceAndDeviceLoss(
|
|||
}
|
||||
|
||||
bool VulkanCommandProcessor::BeginSubmission(bool is_guest_command) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
if (device_lost_) {
|
||||
return false;
|
||||
|
|
@ -2935,9 +2940,9 @@ bool VulkanCommandProcessor::BeginSubmission(bool is_guest_command) {
|
|||
}
|
||||
|
||||
bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
|
||||
ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// Make sure everything needed for submitting exist.
|
||||
if (submission_open_) {
|
||||
|
|
@ -2977,7 +2982,7 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
|
|||
command_pool_create_info.pNext = nullptr;
|
||||
command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
command_pool_create_info.queueFamilyIndex =
|
||||
provider.queue_family_graphics_compute();
|
||||
vulkan_device->queue_family_graphics_compute();
|
||||
if (dfn.vkCreateCommandPool(device, &command_pool_create_info, nullptr,
|
||||
&command_buffer.pool) != VK_SUCCESS) {
|
||||
XELOGE("Failed to create a Vulkan command pool");
|
||||
|
|
@ -3053,10 +3058,11 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
|
|||
bind_sparse_info.pSignalSemaphores = &bind_sparse_semaphore;
|
||||
VkResult bind_sparse_result;
|
||||
{
|
||||
ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition(
|
||||
provider.AcquireQueue(provider.queue_family_sparse_binding(), 0));
|
||||
ui::vulkan::VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device->AcquireQueue(
|
||||
vulkan_device->queue_family_sparse_binding(), 0);
|
||||
bind_sparse_result = dfn.vkQueueBindSparse(
|
||||
queue_acquisition.queue, 1, &bind_sparse_info, VK_NULL_HANDLE);
|
||||
queue_acquisition.queue(), 1, &bind_sparse_info, VK_NULL_HANDLE);
|
||||
}
|
||||
if (bind_sparse_result != VK_SUCCESS) {
|
||||
XELOGE("Failed to submit Vulkan sparse binds");
|
||||
|
|
@ -3123,10 +3129,11 @@ bool VulkanCommandProcessor::EndSubmission(bool is_swap) {
|
|||
}
|
||||
VkResult submit_result;
|
||||
{
|
||||
ui::vulkan::VulkanProvider::QueueAcquisition queue_acquisition(
|
||||
provider.AcquireQueue(provider.queue_family_graphics_compute(), 0));
|
||||
ui::vulkan::VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device->AcquireQueue(
|
||||
vulkan_device->queue_family_graphics_compute(), 0);
|
||||
submit_result =
|
||||
dfn.vkQueueSubmit(queue_acquisition.queue, 1, &submit_info, fence);
|
||||
dfn.vkQueueSubmit(queue_acquisition.queue(), 1, &submit_info, fence);
|
||||
}
|
||||
if (submit_result != VK_SUCCESS) {
|
||||
XELOGE("Failed to submit a Vulkan command buffer");
|
||||
|
|
@ -3235,9 +3242,9 @@ void VulkanCommandProcessor::SplitPendingBarrier() {
|
|||
void VulkanCommandProcessor::DestroyScratchBuffer() {
|
||||
assert_false(scratch_buffer_used_);
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
scratch_buffer_last_usage_submission_ = 0;
|
||||
scratch_buffer_last_access_mask_ = 0;
|
||||
|
|
@ -3252,9 +3259,9 @@ void VulkanCommandProcessor::DestroyScratchBuffer() {
|
|||
void VulkanCommandProcessor::UpdateDynamicState(
|
||||
const draw_util::ViewportInfo& viewport_info, bool primitive_polygonal,
|
||||
reg::RB_DEPTHCONTROL normalized_depth_control) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
const RegisterFile& regs = *register_file_;
|
||||
|
||||
|
|
@ -3358,7 +3365,7 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
|||
if (normalized_depth_control.stencil_enable) {
|
||||
Register stencil_ref_mask_front_reg, stencil_ref_mask_back_reg;
|
||||
if (primitive_polygonal && normalized_depth_control.backface_enable) {
|
||||
if (GetVulkanProvider().device_info().separateStencilMaskRef) {
|
||||
if (GetVulkanDevice()->properties().separateStencilMaskRef) {
|
||||
stencil_ref_mask_front_reg = XE_GPU_REG_RB_STENCILREFMASK;
|
||||
stencil_ref_mask_back_reg = XE_GPU_REG_RB_STENCILREFMASK_BF;
|
||||
} else {
|
||||
|
|
@ -3475,9 +3482,9 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
bool shader_32bit_index_dma, const draw_util::ViewportInfo& viewport_info,
|
||||
uint32_t used_texture_mask, reg::RB_DEPTHCONTROL normalized_depth_control,
|
||||
uint32_t normalized_color_mask) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
const RegisterFile& regs = *register_file_;
|
||||
auto pa_cl_vte_cntl = regs.Get<reg::PA_CL_VTE_CNTL>();
|
||||
|
|
@ -3719,7 +3726,7 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
}
|
||||
|
||||
// Texture host swizzle in the shader.
|
||||
if (!GetVulkanProvider().device_info().imageViewFormatSwizzle) {
|
||||
if (!GetVulkanDevice()->properties().imageViewFormatSwizzle) {
|
||||
uint32_t textures_remaining = used_texture_mask;
|
||||
uint32_t texture_index;
|
||||
while (xe::bit_scan_forward(textures_remaining, &texture_index)) {
|
||||
|
|
@ -3935,15 +3942,15 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
|||
|
||||
bool VulkanCommandProcessor::UpdateBindings(const VulkanShader* vertex_shader,
|
||||
const VulkanShader* pixel_shader) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
const RegisterFile& regs = *register_file_;
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// Invalidate constant buffers and descriptors for changed data.
|
||||
|
||||
|
|
@ -4002,7 +4009,7 @@ bool VulkanCommandProcessor::UpdateBindings(const VulkanShader* vertex_shader,
|
|||
current_graphics_descriptor_set_values_up_to_date_ &=
|
||||
~(UINT32_C(1) << SpirvShaderTranslator::kDescriptorSetConstants);
|
||||
size_t uniform_buffer_alignment =
|
||||
size_t(provider.device_info().minUniformBufferOffsetAlignment);
|
||||
size_t(vulkan_device->properties().minUniformBufferOffsetAlignment);
|
||||
// System constants.
|
||||
if (!(current_constant_buffers_up_to_date_ &
|
||||
(UINT32_C(1) << SpirvShaderTranslator::kConstantBufferSystem))) {
|
||||
|
|
@ -4376,10 +4383,9 @@ uint8_t* VulkanCommandProcessor::WriteTransientUniformBufferBinding(
|
|||
if (descriptor_set == VK_NULL_HANDLE) {
|
||||
return nullptr;
|
||||
}
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
uint8_t* mapping = uniform_buffer_pool_->Request(
|
||||
frame_current_, size,
|
||||
size_t(provider.device_info().minUniformBufferOffsetAlignment),
|
||||
size_t(GetVulkanDevice()->properties().minUniformBufferOffsetAlignment),
|
||||
descriptor_buffer_info_out.buffer, descriptor_buffer_info_out.offset);
|
||||
if (!mapping) {
|
||||
return nullptr;
|
||||
|
|
@ -4409,9 +4415,9 @@ uint8_t* VulkanCommandProcessor::WriteTransientUniformBufferBinding(
|
|||
if (!mapping) {
|
||||
return nullptr;
|
||||
}
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device = GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
dfn.vkUpdateDescriptorSets(device, 1, &write_descriptor_set, 0, nullptr);
|
||||
descriptor_set_out = write_descriptor_set.dstSet;
|
||||
return mapping;
|
||||
|
|
|
|||
|
|
@ -143,9 +143,10 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
|
||||
void RestoreEdramSnapshot(const void* snapshot) override;
|
||||
|
||||
ui::vulkan::VulkanProvider& GetVulkanProvider() const {
|
||||
return *static_cast<ui::vulkan::VulkanProvider*>(
|
||||
graphics_system_->provider());
|
||||
ui::vulkan::VulkanDevice* GetVulkanDevice() const {
|
||||
return static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system_->provider())
|
||||
->vulkan_device();
|
||||
}
|
||||
|
||||
// Returns the deferred drawing command list for the currently open
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ std::string VulkanGraphicsSystem::name() const {
|
|||
X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
|
||||
kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
bool is_surface_required) {
|
||||
provider_ = xe::ui::vulkan::VulkanProvider::Create(is_surface_required);
|
||||
bool with_presentation) {
|
||||
provider_ = xe::ui::vulkan::VulkanProvider::Create(true, with_presentation);
|
||||
return GraphicsSystem::Setup(processor, kernel_state, app_context,
|
||||
is_surface_required);
|
||||
with_presentation);
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandProcessor>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class VulkanGraphicsSystem : public GraphicsSystem {
|
|||
|
||||
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
bool is_surface_required) override;
|
||||
bool with_presentation) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
|
||||
|
|
|
|||
|
|
@ -51,15 +51,15 @@ VulkanPipelineCache::VulkanPipelineCache(
|
|||
VulkanPipelineCache::~VulkanPipelineCache() { Shutdown(); }
|
||||
|
||||
bool VulkanPipelineCache::Initialize() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
|
||||
bool edram_fragment_shader_interlock =
|
||||
render_target_cache_.GetPath() ==
|
||||
RenderTargetCache::Path::kPixelShaderInterlock;
|
||||
|
||||
shader_translator_ = std::make_unique<SpirvShaderTranslator>(
|
||||
SpirvShaderTranslator::Features(provider.device_info()),
|
||||
SpirvShaderTranslator::Features(vulkan_device),
|
||||
render_target_cache_.msaa_2x_attachments_supported(),
|
||||
render_target_cache_.msaa_2x_no_attachments_supported(),
|
||||
edram_fragment_shader_interlock);
|
||||
|
|
@ -68,7 +68,7 @@ bool VulkanPipelineCache::Initialize() {
|
|||
std::vector<uint8_t> depth_only_fragment_shader_code =
|
||||
shader_translator_->CreateDepthOnlyFragmentShader();
|
||||
depth_only_fragment_shader_ = ui::vulkan::util::CreateShaderModule(
|
||||
provider,
|
||||
vulkan_device,
|
||||
reinterpret_cast<const uint32_t*>(
|
||||
depth_only_fragment_shader_code.data()),
|
||||
depth_only_fragment_shader_code.size());
|
||||
|
|
@ -85,10 +85,10 @@ bool VulkanPipelineCache::Initialize() {
|
|||
}
|
||||
|
||||
void VulkanPipelineCache::Shutdown() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// Destroy all pipelines.
|
||||
last_pipeline_ = nullptr;
|
||||
|
|
@ -136,7 +136,7 @@ VulkanShader* VulkanPipelineCache::LoadShader(xenos::ShaderType shader_type,
|
|||
// We need to track it even if it fails translation so we know not to try
|
||||
// again.
|
||||
VulkanShader* shader =
|
||||
new VulkanShader(command_processor_.GetVulkanProvider(), shader_type,
|
||||
new VulkanShader(command_processor_.GetVulkanDevice(), shader_type,
|
||||
data_hash, host_address, dword_count);
|
||||
shaders_.emplace(data_hash, shader);
|
||||
return shader;
|
||||
|
|
@ -268,9 +268,9 @@ bool VulkanPipelineCache::ConfigurePipeline(
|
|||
VulkanRenderTargetCache::RenderPassKey render_pass_key,
|
||||
VkPipeline& pipeline_out,
|
||||
const PipelineLayoutProvider*& pipeline_layout_out) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
// Ensure shaders are translated - needed now for GetCurrentStateDescription.
|
||||
if (!EnsureShadersTranslated(vertex_shader, pixel_shader)) {
|
||||
|
|
@ -471,8 +471,8 @@ void VulkanPipelineCache::WritePipelineRenderTargetDescription(
|
|||
render_target_out.dst_alpha_blend_factor =
|
||||
kBlendFactorMap[uint32_t(blend_control.alpha_destblend)];
|
||||
render_target_out.alpha_blend_op = blend_control.alpha_comb_fcn;
|
||||
if (!command_processor_.GetVulkanProvider()
|
||||
.device_info()
|
||||
if (!command_processor_.GetVulkanDevice()
|
||||
->properties()
|
||||
.constantAlphaColorBlendFactors) {
|
||||
if (blend_control.color_srcblend == xenos::BlendFactor::kConstantAlpha) {
|
||||
render_target_out.src_color_blend_factor =
|
||||
|
|
@ -512,8 +512,8 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
PipelineDescription& description_out) const {
|
||||
description_out.Reset();
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
command_processor_.GetVulkanDevice()->properties();
|
||||
|
||||
const RegisterFile& regs = register_file_;
|
||||
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
|
||||
|
|
@ -548,7 +548,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
break;
|
||||
case xenos::PrimitiveType::kTriangleFan:
|
||||
// The check should be performed at primitive processing time.
|
||||
assert_true(device_info.triangleFans);
|
||||
assert_true(device_properties.triangleFans);
|
||||
primitive_topology = PipelinePrimitiveTopology::kTriangleFan;
|
||||
break;
|
||||
case xenos::PrimitiveType::kTriangleStrip:
|
||||
|
|
@ -572,7 +572,8 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
primitive_processing_result.host_primitive_reset_enabled;
|
||||
|
||||
description_out.depth_clamp_enable =
|
||||
device_info.depthClamp && regs.Get<reg::PA_CL_CLIP_CNTL>().clip_disable;
|
||||
device_properties.depthClamp &&
|
||||
regs.Get<reg::PA_CL_CLIP_CNTL>().clip_disable;
|
||||
|
||||
// TODO(Triang3l): Tessellation.
|
||||
bool primitive_polygonal = draw_util::IsPrimitivePolygonal(regs);
|
||||
|
|
@ -587,7 +588,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
bool cull_back = pa_su_sc_mode_cntl.cull_back;
|
||||
description_out.cull_front = cull_front;
|
||||
description_out.cull_back = cull_back;
|
||||
if (device_info.fillModeNonSolid) {
|
||||
if (device_properties.fillModeNonSolid) {
|
||||
xenos::PolygonType polygon_type = xenos::PolygonType::kTriangles;
|
||||
if (!cull_front) {
|
||||
polygon_type =
|
||||
|
|
@ -604,7 +605,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
case xenos::PolygonType::kPoints:
|
||||
// When points are not supported, use lines instead, preserving
|
||||
// debug-like purpose.
|
||||
description_out.polygon_mode = device_info.pointPolygons
|
||||
description_out.polygon_mode = device_properties.pointPolygons
|
||||
? PipelinePolygonMode::kPoint
|
||||
: PipelinePolygonMode::kLine;
|
||||
break;
|
||||
|
|
@ -671,7 +672,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
// Color blending and write masks (filled only for the attachments present
|
||||
// in the render pass object).
|
||||
uint32_t render_pass_color_rts = render_pass_key.depth_and_color_used >> 1;
|
||||
if (device_info.independentBlend) {
|
||||
assert_true(device_properties.independentBlend);
|
||||
uint32_t render_pass_color_rts_remaining = render_pass_color_rts;
|
||||
uint32_t color_rt_index;
|
||||
while (xe::bit_scan_forward(render_pass_color_rts_remaining,
|
||||
|
|
@ -683,72 +684,6 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
|||
(normalized_color_mask >> (color_rt_index * 4)) & 0b1111,
|
||||
description_out.render_targets[color_rt_index]);
|
||||
}
|
||||
} else {
|
||||
// Take the blend control for the first render target that the guest wants
|
||||
// to write to (consider it the most important) and use it for all render
|
||||
// targets, if any.
|
||||
// TODO(Triang3l): Implement an option for independent blending via
|
||||
// replaying the render pass for each set of render targets with unique
|
||||
// blending parameters, with depth / stencil saved before the first and
|
||||
// restored before each of the rest maybe? Though independent blending
|
||||
// support is pretty wide, with a quite prominent exception of Adreno 4xx
|
||||
// apparently.
|
||||
uint32_t render_pass_color_rts_remaining = render_pass_color_rts;
|
||||
uint32_t render_pass_first_color_rt_index;
|
||||
if (xe::bit_scan_forward(render_pass_color_rts_remaining,
|
||||
&render_pass_first_color_rt_index)) {
|
||||
render_pass_color_rts_remaining &=
|
||||
~(uint32_t(1) << render_pass_first_color_rt_index);
|
||||
PipelineRenderTarget& render_pass_first_color_rt =
|
||||
description_out.render_targets[render_pass_first_color_rt_index];
|
||||
uint32_t common_blend_rt_index;
|
||||
if (xe::bit_scan_forward(normalized_color_mask,
|
||||
&common_blend_rt_index)) {
|
||||
common_blend_rt_index >>= 2;
|
||||
// If a common write mask will be used for multiple render targets,
|
||||
// use the original RB_COLOR_MASK instead of the normalized color mask
|
||||
// as the normalized color mask has non-existent components forced to
|
||||
// written (don't need reading to be preserved), while the number of
|
||||
// components may vary between render targets. The attachments in the
|
||||
// pass that must not be written to at all will be excluded via a
|
||||
// shader modification.
|
||||
WritePipelineRenderTargetDescription(
|
||||
regs.Get<reg::RB_BLENDCONTROL>(
|
||||
reg::RB_BLENDCONTROL::rt_register_indices
|
||||
[common_blend_rt_index]),
|
||||
(((normalized_color_mask &
|
||||
~(uint32_t(0b1111) << (4 * common_blend_rt_index)))
|
||||
? regs[XE_GPU_REG_RB_COLOR_MASK]
|
||||
: normalized_color_mask) >>
|
||||
(4 * common_blend_rt_index)) &
|
||||
0b1111,
|
||||
render_pass_first_color_rt);
|
||||
} else {
|
||||
// No render targets are written to, though the render pass still may
|
||||
// contain color attachments - set them to not written and not
|
||||
// blending.
|
||||
render_pass_first_color_rt.src_color_blend_factor =
|
||||
PipelineBlendFactor::kOne;
|
||||
render_pass_first_color_rt.dst_color_blend_factor =
|
||||
PipelineBlendFactor::kZero;
|
||||
render_pass_first_color_rt.color_blend_op = xenos::BlendOp::kAdd;
|
||||
render_pass_first_color_rt.src_alpha_blend_factor =
|
||||
PipelineBlendFactor::kOne;
|
||||
render_pass_first_color_rt.dst_alpha_blend_factor =
|
||||
PipelineBlendFactor::kZero;
|
||||
render_pass_first_color_rt.alpha_blend_op = xenos::BlendOp::kAdd;
|
||||
}
|
||||
// Reuse the same blending settings for all render targets in the pass,
|
||||
// for description consistency.
|
||||
uint32_t color_rt_index;
|
||||
while (xe::bit_scan_forward(render_pass_color_rts_remaining,
|
||||
&color_rt_index)) {
|
||||
render_pass_color_rts_remaining &= ~(uint32_t(1) << color_rt_index);
|
||||
description_out.render_targets[color_rt_index] =
|
||||
render_pass_first_color_rt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -767,65 +702,37 @@ bool VulkanPipelineCache::ArePipelineRequirementsMet(
|
|||
return false;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
command_processor_.GetVulkanDevice()->properties();
|
||||
|
||||
if (!device_info.geometryShader &&
|
||||
if (!device_properties.geometryShader &&
|
||||
description.geometry_shader != PipelineGeometryShader::kNone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device_info.triangleFans &&
|
||||
if (!device_properties.triangleFans &&
|
||||
description.primitive_topology ==
|
||||
PipelinePrimitiveTopology::kTriangleFan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device_info.depthClamp && description.depth_clamp_enable) {
|
||||
if (!device_properties.depthClamp && description.depth_clamp_enable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device_info.pointPolygons &&
|
||||
if (!device_properties.pointPolygons &&
|
||||
description.polygon_mode == PipelinePolygonMode::kPoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device_info.fillModeNonSolid &&
|
||||
if (!device_properties.fillModeNonSolid &&
|
||||
description.polygon_mode != PipelinePolygonMode::kFill) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device_info.independentBlend) {
|
||||
uint32_t color_rts_remaining =
|
||||
description.render_pass_key.depth_and_color_used >> 1;
|
||||
uint32_t first_color_rt_index;
|
||||
if (xe::bit_scan_forward(color_rts_remaining, &first_color_rt_index)) {
|
||||
color_rts_remaining &= ~(uint32_t(1) << first_color_rt_index);
|
||||
const PipelineRenderTarget& first_color_rt =
|
||||
description.render_targets[first_color_rt_index];
|
||||
uint32_t color_rt_index;
|
||||
while (xe::bit_scan_forward(color_rts_remaining, &color_rt_index)) {
|
||||
color_rts_remaining &= ~(uint32_t(1) << color_rt_index);
|
||||
const PipelineRenderTarget& color_rt =
|
||||
description.render_targets[color_rt_index];
|
||||
if (color_rt.src_color_blend_factor !=
|
||||
first_color_rt.src_color_blend_factor ||
|
||||
color_rt.dst_color_blend_factor !=
|
||||
first_color_rt.dst_color_blend_factor ||
|
||||
color_rt.color_blend_op != first_color_rt.color_blend_op ||
|
||||
color_rt.src_alpha_blend_factor !=
|
||||
first_color_rt.src_alpha_blend_factor ||
|
||||
color_rt.dst_alpha_blend_factor !=
|
||||
first_color_rt.dst_alpha_blend_factor ||
|
||||
color_rt.alpha_blend_op != first_color_rt.alpha_blend_op ||
|
||||
color_rt.color_write_mask != first_color_rt.color_write_mask) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_true(device_properties.independentBlend);
|
||||
|
||||
if (!device_info.constantAlphaColorBlendFactors) {
|
||||
if (!device_properties.constantAlphaColorBlendFactors) {
|
||||
uint32_t color_rts_remaining =
|
||||
description.render_pass_key.depth_and_color_used >> 1;
|
||||
uint32_t color_rt_index;
|
||||
|
|
@ -1849,10 +1756,9 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
|||
|
||||
// Create the shader module, and store the handle even if creation fails not
|
||||
// to try to create it again later.
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
VkShaderModule shader_module = ui::vulkan::util::CreateShaderModule(
|
||||
provider, reinterpret_cast<const uint32_t*>(shader_code.data()),
|
||||
command_processor_.GetVulkanDevice(),
|
||||
reinterpret_cast<const uint32_t*>(shader_code.data()),
|
||||
sizeof(uint32_t) * shader_code.size());
|
||||
if (shader_module == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
|
|
@ -1892,10 +1798,8 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
return false;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
|
||||
bool edram_fragment_shader_interlock =
|
||||
render_target_cache_.GetPath() ==
|
||||
|
|
@ -2174,6 +2078,7 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
VK_BLEND_OP_ADD,
|
||||
VK_BLEND_OP_ADD,
|
||||
VK_BLEND_OP_ADD};
|
||||
assert_true(vulkan_device->properties().independentBlend);
|
||||
uint32_t color_rts_remaining = color_rts_used;
|
||||
uint32_t color_rt_index;
|
||||
while (xe::bit_scan_forward(color_rts_remaining, &color_rt_index)) {
|
||||
|
|
@ -2204,28 +2109,10 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
}
|
||||
color_blend_attachment.colorWriteMask =
|
||||
VkColorComponentFlags(color_rt.color_write_mask);
|
||||
if (!device_info.independentBlend) {
|
||||
// For non-independent blend, the pAttachments element for the first
|
||||
// actually used color will be replicated into all.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
color_blend_state.attachmentCount = 32 - xe::lzcnt(color_rts_used);
|
||||
color_blend_state.pAttachments = color_blend_attachments;
|
||||
if (color_rts_used && !device_info.independentBlend) {
|
||||
// "If the independent blending feature is not enabled, all elements of
|
||||
// pAttachments must be identical."
|
||||
uint32_t first_color_rt_index;
|
||||
xe::bit_scan_forward(color_rts_used, &first_color_rt_index);
|
||||
for (uint32_t i = 0; i < color_blend_state.attachmentCount; ++i) {
|
||||
if (i == first_color_rt_index) {
|
||||
continue;
|
||||
}
|
||||
color_blend_attachments[i] =
|
||||
color_blend_attachments[first_color_rt_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<VkDynamicState, 7> dynamic_states;
|
||||
|
|
@ -2276,8 +2163,8 @@ bool VulkanPipelineCache::EnsurePipelineCreated(
|
|||
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
|
||||
pipeline_create_info.basePipelineIndex = -1;
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
VkPipeline pipeline;
|
||||
if (dfn.vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1,
|
||||
&pipeline_create_info, nullptr,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_render_target_cache.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_api.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include "xenia/base/logging.h"
|
||||
#include "xenia/gpu/vulkan/deferred_command_buffer.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
@ -27,29 +26,30 @@ namespace vulkan {
|
|||
VulkanPrimitiveProcessor::~VulkanPrimitiveProcessor() { Shutdown(true); }
|
||||
|
||||
bool VulkanPrimitiveProcessor::Initialize() {
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
if (!InitializeCommon(device_info.fullDrawIndexUint32,
|
||||
device_info.triangleFans, false,
|
||||
device_info.geometryShader, device_info.geometryShader,
|
||||
device_info.geometryShader)) {
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
vulkan_device->properties();
|
||||
if (!InitializeCommon(
|
||||
device_properties.fullDrawIndexUint32, device_properties.triangleFans,
|
||||
false, device_properties.geometryShader,
|
||||
device_properties.geometryShader, device_properties.geometryShader)) {
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
frame_index_buffer_pool_ =
|
||||
std::make_unique<ui::vulkan::VulkanUploadBufferPool>(
|
||||
command_processor_.GetVulkanProvider(),
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
vulkan_device, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
std::max(size_t(kMinRequiredConvertedIndexBufferSize),
|
||||
ui::GraphicsUploadBufferPool::kDefaultPageSize));
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanPrimitiveProcessor::Shutdown(bool from_destructor) {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
frame_index_buffers_.clear();
|
||||
frame_index_buffer_pool_.reset();
|
||||
|
|
@ -71,10 +71,10 @@ void VulkanPrimitiveProcessor::CompletedSubmissionUpdated() {
|
|||
if (builtin_index_buffer_upload_ != VK_NULL_HANDLE &&
|
||||
command_processor_.GetCompletedSubmission() >=
|
||||
builtin_index_buffer_upload_submission_) {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
||||
builtin_index_buffer_upload_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
||||
|
|
@ -131,14 +131,14 @@ bool VulkanPrimitiveProcessor::InitializeBuiltinIndexBuffer(
|
|||
assert_true(builtin_index_buffer_upload_ == VK_NULL_HANDLE);
|
||||
assert_true(builtin_index_buffer_upload_memory_ == VK_NULL_HANDLE);
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
builtin_index_buffer_size_ = VkDeviceSize(size_bytes);
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider, builtin_index_buffer_size_,
|
||||
vulkan_device, builtin_index_buffer_size_,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal, builtin_index_buffer_,
|
||||
builtin_index_buffer_memory_)) {
|
||||
|
|
@ -150,7 +150,7 @@ bool VulkanPrimitiveProcessor::InitializeBuiltinIndexBuffer(
|
|||
}
|
||||
uint32_t upload_memory_type;
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider, builtin_index_buffer_size_,
|
||||
vulkan_device, builtin_index_buffer_size_,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
ui::vulkan::util::MemoryPurpose::kUpload,
|
||||
builtin_index_buffer_upload_, builtin_index_buffer_upload_memory_,
|
||||
|
|
@ -185,7 +185,7 @@ bool VulkanPrimitiveProcessor::InitializeBuiltinIndexBuffer(
|
|||
}
|
||||
fill_callback(mapping);
|
||||
ui::vulkan::util::FlushMappedMemoryRange(
|
||||
provider, builtin_index_buffer_memory_, upload_memory_type);
|
||||
vulkan_device, builtin_index_buffer_memory_, upload_memory_type);
|
||||
dfn.vkUnmapMemory(device, builtin_index_buffer_upload_memory_);
|
||||
|
||||
// Schedule uploading in the first submission.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/gpu/primitive_processor.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
|
|||
|
|
@ -207,14 +207,15 @@ VulkanRenderTargetCache::VulkanRenderTargetCache(
|
|||
VulkanRenderTargetCache::~VulkanRenderTargetCache() { Shutdown(true); }
|
||||
|
||||
bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::InstanceFunctions& ifn = provider.ifn();
|
||||
VkPhysicalDevice physical_device = provider.physical_device();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanInstance::Functions& ifn =
|
||||
vulkan_device->vulkan_instance()->functions();
|
||||
const VkPhysicalDevice physical_device = vulkan_device->physical_device();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
vulkan_device->properties();
|
||||
|
||||
if (cvars::render_target_path_vulkan == "fsi") {
|
||||
path_ = Path::kPixelShaderInterlock;
|
||||
|
|
@ -243,13 +244,13 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
// between, for instance, the ability to vfetch and memexport in fragment
|
||||
// shaders, and the usage of fragment shader interlock, prefer the former
|
||||
// for simplicity.
|
||||
if (!(device_info.fragmentShaderSampleInterlock ||
|
||||
device_info.fragmentShaderPixelInterlock) ||
|
||||
!device_info.fragmentStoresAndAtomics ||
|
||||
!device_info.sampleRateShading ||
|
||||
!device_info.standardSampleLocations ||
|
||||
if (!(device_properties.fragmentShaderSampleInterlock ||
|
||||
device_properties.fragmentShaderPixelInterlock) ||
|
||||
!device_properties.fragmentStoresAndAtomics ||
|
||||
!device_properties.sampleRateShading ||
|
||||
!device_properties.standardSampleLocations ||
|
||||
shared_memory_binding_count >=
|
||||
device_info.maxPerStageDescriptorStorageBuffers) {
|
||||
device_properties.maxPerStageDescriptorStorageBuffers) {
|
||||
path_ = Path::kHostRenderTargets;
|
||||
}
|
||||
}
|
||||
|
|
@ -271,17 +272,18 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
if (cvars::native_2x_msaa) {
|
||||
// Multisampled integer sampled images are optional in Vulkan and in Xenia.
|
||||
msaa_2x_attachments_supported_ =
|
||||
(device_info.framebufferColorSampleCounts &
|
||||
device_info.framebufferDepthSampleCounts &
|
||||
device_info.framebufferStencilSampleCounts &
|
||||
device_info.sampledImageColorSampleCounts &
|
||||
device_info.sampledImageDepthSampleCounts &
|
||||
device_info.sampledImageStencilSampleCounts & VK_SAMPLE_COUNT_2_BIT) &&
|
||||
(device_info.sampledImageIntegerSampleCounts &
|
||||
(device_properties.framebufferColorSampleCounts &
|
||||
device_properties.framebufferDepthSampleCounts &
|
||||
device_properties.framebufferStencilSampleCounts &
|
||||
device_properties.sampledImageColorSampleCounts &
|
||||
device_properties.sampledImageDepthSampleCounts &
|
||||
device_properties.sampledImageStencilSampleCounts &
|
||||
VK_SAMPLE_COUNT_2_BIT) &&
|
||||
(device_properties.sampledImageIntegerSampleCounts &
|
||||
(VK_SAMPLE_COUNT_2_BIT | VK_SAMPLE_COUNT_4_BIT)) !=
|
||||
VK_SAMPLE_COUNT_4_BIT;
|
||||
msaa_2x_no_attachments_supported_ =
|
||||
(device_info.framebufferNoAttachmentsSampleCounts &
|
||||
(device_properties.framebufferNoAttachmentsSampleCounts &
|
||||
VK_SAMPLE_COUNT_2_BIT) != 0;
|
||||
} else {
|
||||
msaa_2x_attachments_supported_ = false;
|
||||
|
|
@ -349,19 +351,19 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
descriptor_set_layout_size.descriptorCount = 1;
|
||||
descriptor_set_pool_sampled_image_ =
|
||||
std::make_unique<ui::vulkan::SingleLayoutDescriptorSetPool>(
|
||||
provider, 256, 1, &descriptor_set_layout_size,
|
||||
vulkan_device, 256, 1, &descriptor_set_layout_size,
|
||||
descriptor_set_layout_sampled_image_);
|
||||
descriptor_set_layout_size.descriptorCount = 2;
|
||||
descriptor_set_pool_sampled_image_x2_ =
|
||||
std::make_unique<ui::vulkan::SingleLayoutDescriptorSetPool>(
|
||||
provider, 256, 1, &descriptor_set_layout_size,
|
||||
vulkan_device, 256, 1, &descriptor_set_layout_size,
|
||||
descriptor_set_layout_sampled_image_x2_);
|
||||
|
||||
// EDRAM contents reinterpretation buffer.
|
||||
// 90 MB with 9x resolution scaling - within the minimum
|
||||
// maxStorageBufferRange.
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider,
|
||||
vulkan_device,
|
||||
VkDeviceSize(xenos::kEdramSizeBytes *
|
||||
(draw_resolution_scale_x() * draw_resolution_scale_y())),
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
|
|
@ -498,7 +500,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
resolve_copy_shader_code.scaled &&
|
||||
resolve_copy_shader_code.scaled_size_bytes);
|
||||
VkPipeline resolve_copy_pipeline = ui::vulkan::util::CreateComputePipeline(
|
||||
provider, resolve_copy_pipeline_layout_,
|
||||
vulkan_device, resolve_copy_pipeline_layout_,
|
||||
draw_resolution_scaled ? resolve_copy_shader_code.scaled
|
||||
: resolve_copy_shader_code.unscaled,
|
||||
draw_resolution_scaled ? resolve_copy_shader_code.scaled_size_bytes
|
||||
|
|
@ -511,7 +513,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
provider.SetDeviceObjectName(VK_OBJECT_TYPE_PIPELINE, resolve_copy_pipeline,
|
||||
vulkan_device->SetObjectName(VK_OBJECT_TYPE_PIPELINE, resolve_copy_pipeline,
|
||||
resolve_copy_shader_info.debug_name);
|
||||
resolve_copy_pipelines_[i] = resolve_copy_pipeline;
|
||||
}
|
||||
|
|
@ -569,7 +571,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
host_depth_store_shaders[i];
|
||||
VkPipeline host_depth_store_pipeline =
|
||||
ui::vulkan::util::CreateComputePipeline(
|
||||
provider, host_depth_store_pipeline_layout_,
|
||||
vulkan_device, host_depth_store_pipeline_layout_,
|
||||
host_depth_store_shader.first, host_depth_store_shader.second);
|
||||
if (host_depth_store_pipeline == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
|
|
@ -585,7 +587,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
// Transfer and clear vertex buffer, for quads of up to tile granularity.
|
||||
transfer_vertex_buffer_pool_ =
|
||||
std::make_unique<ui::vulkan::VulkanUploadBufferPool>(
|
||||
provider, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
vulkan_device, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
std::max(ui::vulkan::VulkanUploadBufferPool::kDefaultPageSize,
|
||||
sizeof(float) * 2 * 6 *
|
||||
Transfer::kMaxCutoutBorderRectangles *
|
||||
|
|
@ -593,7 +595,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
|
||||
// Transfer vertex shader.
|
||||
transfer_passthrough_vertex_shader_ = ui::vulkan::util::CreateShaderModule(
|
||||
provider, shaders::passthrough_position_xy_vs,
|
||||
vulkan_device, shaders::passthrough_position_xy_vs,
|
||||
sizeof(shaders::passthrough_position_xy_vs));
|
||||
if (transfer_passthrough_vertex_shader_ == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
|
|
@ -757,7 +759,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
return false;
|
||||
}
|
||||
resolve_fsi_clear_32bpp_pipeline_ = ui::vulkan::util::CreateComputePipeline(
|
||||
provider, resolve_fsi_clear_pipeline_layout_,
|
||||
vulkan_device, resolve_fsi_clear_pipeline_layout_,
|
||||
draw_resolution_scaled ? shaders::resolve_clear_32bpp_scaled_cs
|
||||
: shaders::resolve_clear_32bpp_cs,
|
||||
draw_resolution_scaled ? sizeof(shaders::resolve_clear_32bpp_scaled_cs)
|
||||
|
|
@ -770,7 +772,7 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
return false;
|
||||
}
|
||||
resolve_fsi_clear_64bpp_pipeline_ = ui::vulkan::util::CreateComputePipeline(
|
||||
provider, resolve_fsi_clear_pipeline_layout_,
|
||||
vulkan_device, resolve_fsi_clear_pipeline_layout_,
|
||||
draw_resolution_scaled ? shaders::resolve_clear_64bpp_scaled_cs
|
||||
: shaders::resolve_clear_64bpp_cs,
|
||||
draw_resolution_scaled ? sizeof(shaders::resolve_clear_64bpp_scaled_cs)
|
||||
|
|
@ -838,10 +840,10 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
fsi_framebuffer_create_info.pAttachments = nullptr;
|
||||
fsi_framebuffer_create_info.width = std::min(
|
||||
xenos::kTexture2DCubeMaxWidthHeight * draw_resolution_scale_x(),
|
||||
device_info.maxFramebufferWidth);
|
||||
device_properties.maxFramebufferWidth);
|
||||
fsi_framebuffer_create_info.height = std::min(
|
||||
xenos::kTexture2DCubeMaxWidthHeight * draw_resolution_scale_y(),
|
||||
device_info.maxFramebufferHeight);
|
||||
device_properties.maxFramebufferHeight);
|
||||
fsi_framebuffer_create_info.layers = 1;
|
||||
if (dfn.vkCreateFramebuffer(device, &fsi_framebuffer_create_info, nullptr,
|
||||
&fsi_framebuffer_.framebuffer) != VK_SUCCESS) {
|
||||
|
|
@ -873,10 +875,10 @@ bool VulkanRenderTargetCache::Initialize(uint32_t shared_memory_binding_count) {
|
|||
}
|
||||
|
||||
void VulkanRenderTargetCache::Shutdown(bool from_destructor) {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// Destroy all render targets before the descriptor set pool is destroyed -
|
||||
// may happen if shutting down the VulkanRenderTargetCache by destroying it,
|
||||
|
|
@ -985,10 +987,10 @@ void VulkanRenderTargetCache::Shutdown(bool from_destructor) {
|
|||
}
|
||||
|
||||
void VulkanRenderTargetCache::ClearCache() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// Framebuffer objects must be destroyed because they reference views of
|
||||
// attachment images, which may be removed by the common ClearCache.
|
||||
|
|
@ -1044,10 +1046,10 @@ bool VulkanRenderTargetCache::Resolve(const Memory& memory,
|
|||
return true;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
DeferredCommandBuffer& command_buffer =
|
||||
command_processor_.deferred_command_buffer();
|
||||
|
||||
|
|
@ -1554,10 +1556,10 @@ VkRenderPass VulkanRenderTargetCache::GetHostRenderTargetsRenderPass(
|
|||
: 0;
|
||||
render_pass_create_info.pDependencies = subpass_dependencies;
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
VkRenderPass render_pass;
|
||||
if (dfn.vkCreateRenderPass(device, &render_pass_create_info, nullptr,
|
||||
&render_pass) != VK_SUCCESS) {
|
||||
|
|
@ -1644,10 +1646,10 @@ VkFormat VulkanRenderTargetCache::GetColorOwnershipTransferVulkanFormat(
|
|||
}
|
||||
|
||||
VulkanRenderTargetCache::VulkanRenderTarget::~VulkanRenderTarget() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
render_target_cache_.command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
render_target_cache_.command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
ui::vulkan::SingleLayoutDescriptorSetPool& descriptor_set_pool =
|
||||
key().is_depth
|
||||
? *render_target_cache_.descriptor_set_pool_sampled_image_x2_
|
||||
|
|
@ -1671,25 +1673,25 @@ VulkanRenderTargetCache::VulkanRenderTarget::~VulkanRenderTarget() {
|
|||
}
|
||||
|
||||
uint32_t VulkanRenderTargetCache::GetMaxRenderTargetWidth() const {
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
return std::min(device_info.maxFramebufferWidth,
|
||||
device_info.maxImageDimension2D);
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
command_processor_.GetVulkanDevice()->properties();
|
||||
return std::min(device_properties.maxFramebufferWidth,
|
||||
device_properties.maxImageDimension2D);
|
||||
}
|
||||
|
||||
uint32_t VulkanRenderTargetCache::GetMaxRenderTargetHeight() const {
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
return std::min(device_info.maxFramebufferHeight,
|
||||
device_info.maxImageDimension2D);
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
command_processor_.GetVulkanDevice()->properties();
|
||||
return std::min(device_properties.maxFramebufferHeight,
|
||||
device_properties.maxImageDimension2D);
|
||||
}
|
||||
|
||||
RenderTargetCache::RenderTarget* VulkanRenderTargetCache::CreateRenderTarget(
|
||||
RenderTargetKey key) {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// Create the image.
|
||||
|
||||
|
|
@ -1745,7 +1747,7 @@ RenderTargetCache::RenderTarget* VulkanRenderTargetCache::CreateRenderTarget(
|
|||
VkImage image;
|
||||
VkDeviceMemory memory;
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationImage(
|
||||
provider, image_create_info,
|
||||
vulkan_device, image_create_info,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal, image, memory)) {
|
||||
XELOGE(
|
||||
"VulkanRenderTarget: Failed to create a {}x{} {}xMSAA {} render target "
|
||||
|
|
@ -2071,12 +2073,12 @@ VulkanRenderTargetCache::GetHostRenderTargetsFramebuffer(
|
|||
return &it->second;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
vulkan_device->properties();
|
||||
|
||||
VkRenderPass render_pass = GetHostRenderTargetsRenderPass(render_pass_key);
|
||||
if (render_pass == VK_NULL_HANDLE) {
|
||||
|
|
@ -2125,9 +2127,9 @@ VulkanRenderTargetCache::GetHostRenderTargetsFramebuffer(
|
|||
// there's no limit imposed by the sizes of the attachments that have been
|
||||
// created successfully.
|
||||
host_extent.width = std::min(host_extent.width * draw_resolution_scale_x(),
|
||||
device_info.maxFramebufferWidth);
|
||||
device_properties.maxFramebufferWidth);
|
||||
host_extent.height = std::min(host_extent.height * draw_resolution_scale_y(),
|
||||
device_info.maxFramebufferHeight);
|
||||
device_properties.maxFramebufferHeight);
|
||||
framebuffer_create_info.width = host_extent.width;
|
||||
framebuffer_create_info.height = host_extent.height;
|
||||
framebuffer_create_info.layers = 1;
|
||||
|
|
@ -2150,10 +2152,10 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
|
|||
return shader_it->second;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
vulkan_device->properties();
|
||||
|
||||
std::vector<spv::Id> id_vector_temp;
|
||||
std::vector<unsigned int> uint_vector_temp;
|
||||
|
|
@ -2241,7 +2243,7 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
|
|||
// Outputs.
|
||||
bool shader_uses_stencil_reference_output =
|
||||
mode.output == TransferOutput::kDepth &&
|
||||
provider.device_info().ext_VK_EXT_shader_stencil_export;
|
||||
vulkan_device->extensions().ext_EXT_shader_stencil_export;
|
||||
bool dest_color_is_uint = false;
|
||||
uint32_t dest_color_component_count = 0;
|
||||
spv::Id type_fragment_data_component = spv::NoResult;
|
||||
|
|
@ -2477,7 +2479,7 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
|
|||
spv::Id input_sample_id = spv::NoResult;
|
||||
spv::Id spec_const_sample_id = spv::NoResult;
|
||||
if (key.dest_msaa_samples != xenos::MsaaSamples::k1X) {
|
||||
if (device_info.sampleRateShading) {
|
||||
if (device_properties.sampleRateShading) {
|
||||
// One draw for all samples.
|
||||
builder.addCapability(spv::CapabilitySampleRateShading);
|
||||
input_sample_id = builder.createVariable(
|
||||
|
|
@ -2571,7 +2573,7 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
|
|||
// Load the destination sample index.
|
||||
spv::Id dest_sample_id = spv::NoResult;
|
||||
if (key.dest_msaa_samples != xenos::MsaaSamples::k1X) {
|
||||
if (device_info.sampleRateShading) {
|
||||
if (device_properties.sampleRateShading) {
|
||||
assert_true(input_sample_id != spv::NoResult);
|
||||
dest_sample_id = builder.createUnaryOp(
|
||||
spv::OpBitcast, type_uint,
|
||||
|
|
@ -4194,7 +4196,7 @@ VkShaderModule VulkanRenderTargetCache::GetTransferShader(
|
|||
// Create the shader module, and store the handle even if creation fails not
|
||||
// to try to create it again later.
|
||||
VkShaderModule shader_module = ui::vulkan::util::CreateShaderModule(
|
||||
provider, reinterpret_cast<const uint32_t*>(shader_code.data()),
|
||||
vulkan_device, reinterpret_cast<const uint32_t*>(shader_code.data()),
|
||||
sizeof(uint32_t) * shader_code.size());
|
||||
if (shader_module == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
|
|
@ -4225,17 +4227,17 @@ VkPipeline const* VulkanRenderTargetCache::GetTransferPipelines(
|
|||
|
||||
const TransferModeInfo& mode = kTransferModes[size_t(key.shader_key.mode)];
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
vulkan_device->properties();
|
||||
|
||||
uint32_t dest_sample_count = uint32_t(1)
|
||||
<< uint32_t(key.shader_key.dest_msaa_samples);
|
||||
bool dest_is_masked_sample =
|
||||
dest_sample_count > 1 && !device_info.sampleRateShading;
|
||||
dest_sample_count > 1 && !device_properties.sampleRateShading;
|
||||
|
||||
VkPipelineShaderStageCreateInfo shader_stages[2];
|
||||
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
|
|
@ -4327,7 +4329,7 @@ VkPipeline const* VulkanRenderTargetCache::GetTransferPipelines(
|
|||
? VK_SAMPLE_COUNT_4_BIT
|
||||
: VkSampleCountFlagBits(dest_sample_count);
|
||||
if (dest_sample_count > 1) {
|
||||
if (device_info.sampleRateShading) {
|
||||
if (device_properties.sampleRateShading) {
|
||||
multisample_state.sampleShadingEnable = VK_TRUE;
|
||||
multisample_state.minSampleShading = 1.0f;
|
||||
if (dest_sample_count == 2 && !msaa_2x_attachments_supported_) {
|
||||
|
|
@ -4358,7 +4360,7 @@ VkPipeline const* VulkanRenderTargetCache::GetTransferPipelines(
|
|||
: VK_COMPARE_OP_ALWAYS;
|
||||
}
|
||||
if ((mode.output == TransferOutput::kDepth &&
|
||||
provider.device_info().ext_VK_EXT_shader_stencil_export) ||
|
||||
vulkan_device->extensions().ext_EXT_shader_stencil_export) ||
|
||||
mode.output == TransferOutput::kStencilBit) {
|
||||
depth_stencil_state.stencilTestEnable = VK_TRUE;
|
||||
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
|
|
@ -4386,22 +4388,11 @@ VkPipeline const* VulkanRenderTargetCache::GetTransferPipelines(
|
|||
32 - xe::lzcnt(key.render_pass_key.depth_and_color_used >> 1);
|
||||
color_blend_state.pAttachments = color_blend_attachments;
|
||||
if (mode.output == TransferOutput::kColor) {
|
||||
if (device_info.independentBlend) {
|
||||
// State the intention more explicitly.
|
||||
color_blend_attachments[key.shader_key.dest_color_rt_index]
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
|
||||
VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
} else {
|
||||
// The blend state for all attachments must be identical, but other render
|
||||
// targets are not written to by the shader.
|
||||
for (uint32_t i = 0; i < color_blend_state.attachmentCount; ++i) {
|
||||
color_blend_attachments[i].colorWriteMask =
|
||||
assert_true(device_properties.independentBlend);
|
||||
color_blend_attachments[key.shader_key.dest_color_rt_index].colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<VkDynamicState, 3> dynamic_states;
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state;
|
||||
|
|
@ -4493,8 +4484,8 @@ void VulkanRenderTargetCache::PerformTransfersAndResolveClears(
|
|||
const Transfer::Rectangle* resolve_clear_rectangle) {
|
||||
assert_true(GetPath() == Path::kHostRenderTargets);
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
uint64_t current_submission = command_processor_.GetCurrentSubmission();
|
||||
DeferredCommandBuffer& command_buffer =
|
||||
command_processor_.deferred_command_buffer();
|
||||
|
|
@ -4809,7 +4800,8 @@ void VulkanRenderTargetCache::PerformTransfersAndResolveClears(
|
|||
// Gather shader keys and sort to reduce pipeline state and binding
|
||||
// switches. Also gather stencil rectangles to clear if needed.
|
||||
bool need_stencil_bit_draws =
|
||||
dest_rt_key.is_depth && !device_info.ext_VK_EXT_shader_stencil_export;
|
||||
dest_rt_key.is_depth &&
|
||||
!vulkan_device->extensions().ext_EXT_shader_stencil_export;
|
||||
current_transfer_invocations_.clear();
|
||||
current_transfer_invocations_.reserve(
|
||||
current_transfers.size() << uint32_t(need_stencil_bit_draws));
|
||||
|
|
@ -5001,10 +4993,10 @@ void VulkanRenderTargetCache::PerformTransfersAndResolveClears(
|
|||
transfer_viewport.y = 0.0f;
|
||||
transfer_viewport.width =
|
||||
float(std::min(xe::next_pow2(transfer_framebuffer->host_extent.width),
|
||||
device_info.maxViewportDimensions[0]));
|
||||
vulkan_device->properties().maxViewportDimensions[0]));
|
||||
transfer_viewport.height = float(
|
||||
std::min(xe::next_pow2(transfer_framebuffer->host_extent.height),
|
||||
device_info.maxViewportDimensions[1]));
|
||||
vulkan_device->properties().maxViewportDimensions[1]));
|
||||
transfer_viewport.minDepth = 0.0f;
|
||||
transfer_viewport.maxDepth = 1.0f;
|
||||
command_processor_.SetViewport(transfer_viewport);
|
||||
|
|
@ -5055,7 +5047,7 @@ void VulkanRenderTargetCache::PerformTransfersAndResolveClears(
|
|||
kTransferPipelineLayoutInfos[size_t(
|
||||
transfer_pipeline_layout_index)];
|
||||
uint32_t transfer_sample_pipeline_count =
|
||||
device_info.sampleRateShading
|
||||
vulkan_device->properties().sampleRateShading
|
||||
? 1
|
||||
: uint32_t(1) << uint32_t(dest_rt_key.msaa_samples);
|
||||
bool transfer_is_stencil_bit =
|
||||
|
|
@ -5922,7 +5914,7 @@ VkPipeline VulkanRenderTargetCache::GetDumpPipeline(DumpPipelineKey key) {
|
|||
// Create the pipeline, and store the handle even if creation fails not to try
|
||||
// to create it again later.
|
||||
VkPipeline pipeline = ui::vulkan::util::CreateComputePipeline(
|
||||
command_processor_.GetVulkanProvider(),
|
||||
command_processor_.GetVulkanDevice(),
|
||||
key.is_depth ? dump_pipeline_layout_depth_ : dump_pipeline_layout_color_,
|
||||
reinterpret_cast<const uint32_t*>(shader_code.data()),
|
||||
sizeof(uint32_t) * shader_code.size());
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_texture_cache.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/single_layout_descriptor_set_pool.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
|
|
@ -20,10 +21,10 @@ namespace vulkan {
|
|||
|
||||
VulkanShader::VulkanTranslation::~VulkanTranslation() {
|
||||
if (shader_module_) {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
static_cast<const VulkanShader&>(shader()).provider_;
|
||||
provider.dfn().vkDestroyShaderModule(provider.device(), shader_module_,
|
||||
nullptr);
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
static_cast<const VulkanShader&>(shader()).vulkan_device_;
|
||||
vulkan_device->functions().vkDestroyShaderModule(vulkan_device->device(),
|
||||
shader_module_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,8 +35,8 @@ VkShaderModule VulkanShader::VulkanTranslation::GetOrCreateShaderModule() {
|
|||
if (shader_module_ != VK_NULL_HANDLE) {
|
||||
return shader_module_;
|
||||
}
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
static_cast<const VulkanShader&>(shader()).provider_;
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
static_cast<const VulkanShader&>(shader()).vulkan_device_;
|
||||
VkShaderModuleCreateInfo shader_module_create_info;
|
||||
shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
shader_module_create_info.pNext = nullptr;
|
||||
|
|
@ -43,8 +44,8 @@ VkShaderModule VulkanShader::VulkanTranslation::GetOrCreateShaderModule() {
|
|||
shader_module_create_info.codeSize = translated_binary().size();
|
||||
shader_module_create_info.pCode =
|
||||
reinterpret_cast<const uint32_t*>(translated_binary().data());
|
||||
if (provider.dfn().vkCreateShaderModule(provider.device(),
|
||||
&shader_module_create_info, nullptr,
|
||||
if (vulkan_device->functions().vkCreateShaderModule(
|
||||
vulkan_device->device(), &shader_module_create_info, nullptr,
|
||||
&shader_module_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanShader::VulkanTranslation: Failed to create a Vulkan shader "
|
||||
|
|
@ -56,15 +57,17 @@ VkShaderModule VulkanShader::VulkanTranslation::GetOrCreateShaderModule() {
|
|||
return shader_module_;
|
||||
}
|
||||
|
||||
VulkanShader::VulkanShader(const ui::vulkan::VulkanProvider& provider,
|
||||
xenos::ShaderType shader_type,
|
||||
uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords,
|
||||
size_t ucode_dword_count,
|
||||
std::endian ucode_source_endian)
|
||||
VulkanShader::VulkanShader(const ui::vulkan::VulkanDevice* const vulkan_device,
|
||||
const xenos::ShaderType shader_type,
|
||||
const uint64_t ucode_data_hash,
|
||||
const uint32_t* const ucode_dwords,
|
||||
const size_t ucode_dword_count,
|
||||
const std::endian ucode_source_endian)
|
||||
: SpirvShader(shader_type, ucode_data_hash, ucode_dwords, ucode_dword_count,
|
||||
ucode_source_endian),
|
||||
provider_(provider) {}
|
||||
vulkan_device_(vulkan_device) {
|
||||
assert_not_null(vulkan_device);
|
||||
}
|
||||
|
||||
Shader::Translation* VulkanShader::CreateTranslationInstance(
|
||||
uint64_t modification) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include "xenia/gpu/spirv_shader.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
|
@ -35,7 +35,7 @@ class VulkanShader : public SpirvShader {
|
|||
VkShaderModule shader_module_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
explicit VulkanShader(const ui::vulkan::VulkanProvider& provider,
|
||||
explicit VulkanShader(const ui::vulkan::VulkanDevice* vulkan_device,
|
||||
xenos::ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
const uint32_t* ucode_dwords, size_t ucode_dword_count,
|
||||
std::endian ucode_source_endian = std::endian::big);
|
||||
|
|
@ -67,7 +67,7 @@ class VulkanShader : public SpirvShader {
|
|||
Translation* CreateTranslationInstance(uint64_t modification) override;
|
||||
|
||||
private:
|
||||
const ui::vulkan::VulkanProvider& provider_;
|
||||
const ui::vulkan::VulkanDevice* vulkan_device_;
|
||||
|
||||
std::atomic_flag binding_layout_user_uids_set_up_ = ATOMIC_FLAG_INIT;
|
||||
size_t texture_binding_layout_user_uid_ = 0;
|
||||
|
|
|
|||
|
|
@ -47,12 +47,10 @@ VulkanSharedMemory::~VulkanSharedMemory() { Shutdown(true); }
|
|||
bool VulkanSharedMemory::Initialize() {
|
||||
InitializeCommon();
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
const VkBufferCreateFlags sparse_flags =
|
||||
VK_BUFFER_CREATE_SPARSE_BINDING_BIT |
|
||||
|
|
@ -70,14 +68,15 @@ bool VulkanSharedMemory::Initialize() {
|
|||
buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
buffer_create_info.queueFamilyIndexCount = 0;
|
||||
buffer_create_info.pQueueFamilyIndices = nullptr;
|
||||
if (cvars::vulkan_sparse_shared_memory && device_info.sparseResidencyBuffer) {
|
||||
if (cvars::vulkan_sparse_shared_memory &&
|
||||
vulkan_device->properties().sparseResidencyBuffer) {
|
||||
if (dfn.vkCreateBuffer(device, &buffer_create_info, nullptr, &buffer_) ==
|
||||
VK_SUCCESS) {
|
||||
VkMemoryRequirements buffer_memory_requirements;
|
||||
dfn.vkGetBufferMemoryRequirements(device, buffer_,
|
||||
&buffer_memory_requirements);
|
||||
if (xe::bit_scan_forward(buffer_memory_requirements.memoryTypeBits &
|
||||
device_info.memory_types_device_local,
|
||||
vulkan_device->memory_types().device_local,
|
||||
&buffer_memory_type_)) {
|
||||
uint32_t allocation_size_log2;
|
||||
xe::bit_scan_forward(
|
||||
|
|
@ -130,7 +129,7 @@ bool VulkanSharedMemory::Initialize() {
|
|||
dfn.vkGetBufferMemoryRequirements(device, buffer_,
|
||||
&buffer_memory_requirements);
|
||||
if (!xe::bit_scan_forward(buffer_memory_requirements.memoryTypeBits &
|
||||
device_info.memory_types_device_local,
|
||||
vulkan_device->memory_types().device_local,
|
||||
&buffer_memory_type_)) {
|
||||
XELOGE(
|
||||
"Shared memory: Failed to get a device-local Vulkan memory type for "
|
||||
|
|
@ -147,7 +146,7 @@ bool VulkanSharedMemory::Initialize() {
|
|||
buffer_memory_requirements.size;
|
||||
buffer_memory_allocate_info.memoryTypeIndex = buffer_memory_type_;
|
||||
VkMemoryDedicatedAllocateInfo buffer_memory_dedicated_allocate_info;
|
||||
if (provider.device_info().ext_1_1_VK_KHR_dedicated_allocation) {
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_dedicated_allocation) {
|
||||
buffer_memory_allocate_info_last->pNext =
|
||||
&buffer_memory_dedicated_allocate_info;
|
||||
buffer_memory_allocate_info_last =
|
||||
|
|
@ -183,7 +182,7 @@ bool VulkanSharedMemory::Initialize() {
|
|||
last_written_range_ = std::make_pair<uint32_t, uint32_t>(0, 0);
|
||||
|
||||
upload_buffer_pool_ = std::make_unique<ui::vulkan::VulkanUploadBufferPool>(
|
||||
provider, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
vulkan_device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
xe::align(ui::vulkan::VulkanUploadBufferPool::kDefaultPageSize,
|
||||
size_t(1) << page_size_log2()));
|
||||
|
||||
|
|
@ -195,10 +194,10 @@ void VulkanSharedMemory::Shutdown(bool from_destructor) {
|
|||
|
||||
upload_buffer_pool_.reset();
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device, buffer_);
|
||||
for (VkDeviceMemory memory : buffer_memory_) {
|
||||
|
|
@ -260,10 +259,9 @@ bool VulkanSharedMemory::InitializeTraceSubmitDownloads() {
|
|||
return false;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider, download_page_count << page_size_log2(),
|
||||
command_processor_.GetVulkanDevice(),
|
||||
download_page_count << page_size_log2(),
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
ui::vulkan::util::MemoryPurpose::kReadback, trace_download_buffer_,
|
||||
trace_download_buffer_memory_)) {
|
||||
|
|
@ -306,10 +304,10 @@ void VulkanSharedMemory::InitializeTraceCompleteDownloads() {
|
|||
if (!trace_download_buffer_memory_) {
|
||||
return;
|
||||
}
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
void* download_mapping;
|
||||
if (dfn.vkMapMemory(device, trace_download_buffer_memory_, 0, VK_WHOLE_SIZE,
|
||||
0, &download_mapping) == VK_SUCCESS) {
|
||||
|
|
@ -335,10 +333,10 @@ bool VulkanSharedMemory::AllocateSparseHostGpuMemoryRange(
|
|||
return true;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
VkMemoryAllocateInfo memory_allocate_info;
|
||||
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
|
|
@ -365,7 +363,7 @@ bool VulkanSharedMemory::AllocateSparseHostGpuMemoryRange(
|
|||
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
if (provider.device_info().tessellationShader) {
|
||||
if (vulkan_device->properties().tessellationShader) {
|
||||
bind_wait_stage_mask |=
|
||||
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
|
||||
}
|
||||
|
|
@ -482,10 +480,10 @@ void VulkanSharedMemory::GetUsageMasks(Usage usage,
|
|||
}
|
||||
|
||||
void VulkanSharedMemory::ResetTraceDownload() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
||||
trace_download_buffer_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include "xenia/gpu/shared_memory.h"
|
||||
#include "xenia/gpu/trace_writer.h"
|
||||
#include "xenia/memory.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "xenia/gpu/texture_util.h"
|
||||
#include "xenia/gpu/vulkan/deferred_command_buffer.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_command_processor.h"
|
||||
#include "xenia/ui/vulkan/ui_samplers.h"
|
||||
#include "xenia/ui/vulkan/vulkan_mem_alloc.h"
|
||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||
|
||||
|
|
@ -423,10 +424,10 @@ const VulkanTextureCache::HostFormatPair
|
|||
true};
|
||||
|
||||
VulkanTextureCache::~VulkanTextureCache() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
for (const std::pair<const SamplerParameters, Sampler>& sampler_pair :
|
||||
samplers_) {
|
||||
|
|
@ -523,9 +524,9 @@ void VulkanTextureCache::BeginSubmission(uint64_t new_submission_index) {
|
|||
}
|
||||
|
||||
void VulkanTextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
#endif // XE_GPU_FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
TextureCache::RequestTextures(used_texture_mask);
|
||||
|
||||
|
|
@ -718,10 +719,10 @@ VkSampler VulkanTextureCache::UseSampler(SamplerParameters parameters,
|
|||
return sampler.second.sampler;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// See if an existing sampler can be destroyed to create space for the new
|
||||
// one.
|
||||
|
|
@ -761,7 +762,7 @@ VkSampler VulkanTextureCache::UseSampler(SamplerParameters parameters,
|
|||
// GetSamplerParameters.
|
||||
VkSamplerCreateInfo sampler_create_info = {};
|
||||
sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
if (provider.device_info().nonSeamlessCubeMap &&
|
||||
if (vulkan_device->properties().nonSeamlessCubeMap &&
|
||||
cvars::non_seamless_cube_map) {
|
||||
sampler_create_info.flags |=
|
||||
VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT;
|
||||
|
|
@ -940,17 +941,17 @@ uint32_t VulkanTextureCache::GetHostFormatSwizzle(TextureKey key) const {
|
|||
|
||||
uint32_t VulkanTextureCache::GetMaxHostTextureWidthHeight(
|
||||
xenos::DataDimension dimension) const {
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
command_processor_.GetVulkanDevice()->properties();
|
||||
switch (dimension) {
|
||||
case xenos::DataDimension::k1D:
|
||||
case xenos::DataDimension::k2DOrStacked:
|
||||
// 1D and 2D are emulated as 2D arrays.
|
||||
return device_info.maxImageDimension2D;
|
||||
return device_properties.maxImageDimension2D;
|
||||
case xenos::DataDimension::k3D:
|
||||
return device_info.maxImageDimension3D;
|
||||
return device_properties.maxImageDimension3D;
|
||||
case xenos::DataDimension::kCube:
|
||||
return device_info.maxImageDimensionCube;
|
||||
return device_properties.maxImageDimensionCube;
|
||||
default:
|
||||
assert_unhandled_case(dimension);
|
||||
return 0;
|
||||
|
|
@ -959,15 +960,15 @@ uint32_t VulkanTextureCache::GetMaxHostTextureWidthHeight(
|
|||
|
||||
uint32_t VulkanTextureCache::GetMaxHostTextureDepthOrArraySize(
|
||||
xenos::DataDimension dimension) const {
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
command_processor_.GetVulkanProvider().device_info();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
command_processor_.GetVulkanDevice()->properties();
|
||||
switch (dimension) {
|
||||
case xenos::DataDimension::k1D:
|
||||
case xenos::DataDimension::k2DOrStacked:
|
||||
// 1D and 2D are emulated as 2D arrays.
|
||||
return device_info.maxImageArrayLayers;
|
||||
return device_properties.maxImageArrayLayers;
|
||||
case xenos::DataDimension::k3D:
|
||||
return device_info.maxImageDimension3D;
|
||||
return device_properties.maxImageDimension3D;
|
||||
case xenos::DataDimension::kCube:
|
||||
// Not requesting the imageCubeArray feature, and the Xenos doesn't
|
||||
// support cube map arrays.
|
||||
|
|
@ -1009,10 +1010,10 @@ std::unique_ptr<TextureCache::Texture> VulkanTextureCache::CreateTexture(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
bool is_3d = key.dimension == xenos::DataDimension::k3D;
|
||||
uint32_t depth_or_array_size = key.GetDepthOrArraySize();
|
||||
|
|
@ -1049,7 +1050,7 @@ std::unique_ptr<TextureCache::Texture> VulkanTextureCache::CreateTexture(
|
|||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkImageFormatListCreateInfo image_format_list_create_info;
|
||||
if (formats[1] != VK_FORMAT_UNDEFINED &&
|
||||
provider.device_info().ext_1_2_VK_KHR_image_format_list) {
|
||||
vulkan_device->extensions().ext_1_2_KHR_image_format_list) {
|
||||
image_create_info_last->pNext = &image_format_list_create_info;
|
||||
image_create_info_last =
|
||||
reinterpret_cast<VkImageCreateInfo*>(&image_format_list_create_info);
|
||||
|
|
@ -1224,10 +1225,10 @@ bool VulkanTextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
|
|||
// Begin loading.
|
||||
// TODO(Triang3l): Going from one descriptor to another on per-array-layer
|
||||
// or even per-8-depth-slices level to stay within maxStorageBufferRange.
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
VulkanSharedMemory& vulkan_shared_memory =
|
||||
static_cast<VulkanSharedMemory&>(shared_memory());
|
||||
std::array<VkWriteDescriptorSet, 3> write_descriptor_sets;
|
||||
|
|
@ -1590,10 +1591,10 @@ VulkanTextureCache::VulkanTexture::VulkanTexture(
|
|||
VulkanTextureCache::VulkanTexture::~VulkanTexture() {
|
||||
const VulkanTextureCache& vulkan_texture_cache =
|
||||
static_cast<const VulkanTextureCache&>(texture_cache());
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
vulkan_texture_cache.command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
vulkan_texture_cache.command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
for (const auto& view_pair : views_) {
|
||||
dfn.vkDestroyImageView(device, view_pair.second, nullptr);
|
||||
}
|
||||
|
|
@ -1631,9 +1632,10 @@ VkImageView VulkanTextureCache::VulkanTexture::GetView(bool is_signed,
|
|||
is_signed && (host_format_pair.format_signed.format !=
|
||||
host_format_pair.format_unsigned.format);
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
vulkan_texture_cache.command_processor_.GetVulkanProvider();
|
||||
if (!provider.device_info().imageViewFormatSwizzle) {
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
vulkan_texture_cache.command_processor_.GetVulkanDevice();
|
||||
|
||||
if (!vulkan_device->properties().imageViewFormatSwizzle) {
|
||||
host_swizzle = xenos::XE_GPU_TEXTURE_SWIZZLE_RGBA;
|
||||
}
|
||||
view_key.host_swizzle = host_swizzle;
|
||||
|
|
@ -1647,8 +1649,8 @@ VkImageView VulkanTextureCache::VulkanTexture::GetView(bool is_signed,
|
|||
}
|
||||
|
||||
// Create a new view.
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
VkImageViewCreateInfo view_create_info;
|
||||
view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
view_create_info.pNext = nullptr;
|
||||
|
|
@ -1704,18 +1706,19 @@ VulkanTextureCache::VulkanTextureCache(
|
|||
}
|
||||
|
||||
bool VulkanTextureCache::Initialize() {
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const ui::vulkan::VulkanProvider::InstanceFunctions& ifn = provider.ifn();
|
||||
VkPhysicalDevice physical_device = provider.physical_device();
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const ui::vulkan::VulkanProvider::DeviceInfo& device_info =
|
||||
provider.device_info();
|
||||
const ui::vulkan::VulkanDevice* const vulkan_device =
|
||||
command_processor_.GetVulkanDevice();
|
||||
const ui::vulkan::VulkanInstance::Functions& ifn =
|
||||
vulkan_device->vulkan_instance()->functions();
|
||||
const VkPhysicalDevice physical_device = vulkan_device->physical_device();
|
||||
const ui::vulkan::VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
const ui::vulkan::VulkanDevice::Properties& device_properties =
|
||||
vulkan_device->properties();
|
||||
|
||||
// Vulkan Memory Allocator.
|
||||
|
||||
vma_allocator_ = ui::vulkan::CreateVmaAllocator(provider, true);
|
||||
vma_allocator_ = ui::vulkan::CreateVmaAllocator(vulkan_device, true);
|
||||
if (vma_allocator_ == VK_NULL_HANDLE) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2323,7 +2326,7 @@ bool VulkanTextureCache::Initialize() {
|
|||
load_shader_code[i];
|
||||
assert_not_null(current_load_shader_code.first);
|
||||
load_pipelines_[i] = ui::vulkan::util::CreateComputePipeline(
|
||||
provider, load_pipeline_layout_, current_load_shader_code.first,
|
||||
vulkan_device, load_pipeline_layout_, current_load_shader_code.first,
|
||||
current_load_shader_code.second);
|
||||
if (load_pipelines_[i] == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
|
|
@ -2337,7 +2340,7 @@ bool VulkanTextureCache::Initialize() {
|
|||
current_load_shader_code_scaled = load_shader_code_scaled[i];
|
||||
if (current_load_shader_code_scaled.first) {
|
||||
load_pipelines_scaled_[i] = ui::vulkan::util::CreateComputePipeline(
|
||||
provider, load_pipeline_layout_,
|
||||
vulkan_device, load_pipeline_layout_,
|
||||
current_load_shader_code_scaled.first,
|
||||
current_load_shader_code_scaled.second);
|
||||
if (load_pipelines_scaled_[i] == VK_NULL_HANDLE) {
|
||||
|
|
@ -2402,7 +2405,7 @@ bool VulkanTextureCache::Initialize() {
|
|||
dfn.vkGetImageMemoryRequirements(device, null_image_3d_,
|
||||
&null_image_memory_requirements_3d_);
|
||||
uint32_t null_image_memory_type_common = ui::vulkan::util::ChooseMemoryType(
|
||||
provider,
|
||||
vulkan_device->memory_types(),
|
||||
null_image_memory_requirements_2d_array_cube_.memoryTypeBits &
|
||||
null_image_memory_requirements_3d_.memoryTypeBits,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal);
|
||||
|
|
@ -2443,16 +2446,18 @@ bool VulkanTextureCache::Initialize() {
|
|||
}
|
||||
} else {
|
||||
// Place each null image in separate allocations.
|
||||
uint32_t null_image_memory_type_2d_array_cube_ =
|
||||
const uint32_t null_image_memory_type_2d_array_cube =
|
||||
ui::vulkan::util::ChooseMemoryType(
|
||||
provider,
|
||||
vulkan_device->memory_types(),
|
||||
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,
|
||||
const uint32_t null_image_memory_type_3d =
|
||||
ui::vulkan::util::ChooseMemoryType(
|
||||
vulkan_device->memory_types(),
|
||||
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) {
|
||||
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");
|
||||
|
|
@ -2468,9 +2473,9 @@ bool VulkanTextureCache::Initialize() {
|
|||
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_;
|
||||
null_image_memory_type_2d_array_cube;
|
||||
VkMemoryDedicatedAllocateInfo null_image_memory_dedicated_allocate_info;
|
||||
if (device_info.ext_1_1_VK_KHR_dedicated_allocation) {
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_dedicated_allocation) {
|
||||
null_image_memory_allocate_info_last->pNext =
|
||||
&null_image_memory_dedicated_allocate_info;
|
||||
null_image_memory_allocate_info_last =
|
||||
|
|
@ -2500,8 +2505,7 @@ bool VulkanTextureCache::Initialize() {
|
|||
|
||||
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_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) {
|
||||
|
|
@ -2531,7 +2535,7 @@ bool VulkanTextureCache::Initialize() {
|
|||
// constant components instead of the real texels. The image will be cleared
|
||||
// to (0, 0, 0, 0) anyway.
|
||||
VkComponentSwizzle null_image_view_swizzle =
|
||||
device_info.imageViewFormatSwizzle ? VK_COMPONENT_SWIZZLE_ZERO
|
||||
device_properties.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;
|
||||
|
|
@ -2571,15 +2575,15 @@ bool VulkanTextureCache::Initialize() {
|
|||
// VkDevice (true in a regular emulation scenario), so taking over all the
|
||||
// allocation slots exclusively.
|
||||
// Also leaving a few slots for use by things like overlay applications.
|
||||
sampler_max_count_ =
|
||||
device_info.maxSamplerAllocationCount -
|
||||
uint32_t(ui::vulkan::VulkanProvider::HostSampler::kCount) - 16;
|
||||
sampler_max_count_ = device_properties.maxSamplerAllocationCount -
|
||||
ui::vulkan::UISamplers::kSamplerCount - 16;
|
||||
|
||||
if (device_info.samplerAnisotropy) {
|
||||
if (device_properties.samplerAnisotropy) {
|
||||
max_anisotropy_ = xenos::AnisoFilter(
|
||||
uint32_t(xenos::AnisoFilter::kMax_1_1) +
|
||||
(31 - xe::lzcnt(uint32_t(std::min(
|
||||
16.0f, std::max(1.0f, device_info.maxSamplerAnisotropy))))));
|
||||
(31 -
|
||||
xe::lzcnt(uint32_t(std::min(
|
||||
16.0f, std::max(1.0f, device_properties.maxSamplerAnisotropy))))));
|
||||
} else {
|
||||
max_anisotropy_ = xenos::AnisoFilter::kDisabled;
|
||||
}
|
||||
|
|
@ -2643,8 +2647,8 @@ xenos::ClampMode VulkanTextureCache::NormalizeClampMode(
|
|||
clamp_mode == xenos::ClampMode::kMirrorClampToHalfway ||
|
||||
clamp_mode == xenos::ClampMode::kMirrorClampToBorder) {
|
||||
// No equivalents for anything other than kMirrorClampToEdge in Vulkan.
|
||||
return command_processor_.GetVulkanProvider()
|
||||
.device_info()
|
||||
return command_processor_.GetVulkanDevice()
|
||||
->properties()
|
||||
.samplerMirrorClampToEdge
|
||||
? xenos::ClampMode::kMirrorClampToEdge
|
||||
: xenos::ClampMode::kMirroredRepeat;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
||||
#include "xenia/ui/vulkan/vulkan_mem_alloc.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
|
|
|||
|
|
@ -27,24 +27,24 @@ class VulkanTraceDump : public TraceDump {
|
|||
}
|
||||
|
||||
void BeginHostCapture() override {
|
||||
const RENDERDOC_API_1_0_0* renderdoc_api =
|
||||
const ui::RenderDocAPI* const renderdoc_api =
|
||||
static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system_->provider())
|
||||
->renderdoc_api()
|
||||
.api_1_0_0();
|
||||
if (renderdoc_api && !renderdoc_api->IsFrameCapturing()) {
|
||||
renderdoc_api->StartFrameCapture(nullptr, nullptr);
|
||||
->vulkan_instance()
|
||||
->renderdoc_api();
|
||||
if (renderdoc_api && !renderdoc_api->api_1_0_0()->IsFrameCapturing()) {
|
||||
renderdoc_api->api_1_0_0()->StartFrameCapture(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void EndHostCapture() override {
|
||||
const RENDERDOC_API_1_0_0* renderdoc_api =
|
||||
const ui::RenderDocAPI* const renderdoc_api =
|
||||
static_cast<const ui::vulkan::VulkanProvider*>(
|
||||
graphics_system_->provider())
|
||||
->renderdoc_api()
|
||||
.api_1_0_0();
|
||||
if (renderdoc_api && renderdoc_api->IsFrameCapturing()) {
|
||||
renderdoc_api->EndFrameCapture(nullptr, nullptr);
|
||||
->vulkan_instance()
|
||||
->renderdoc_api();
|
||||
if (renderdoc_api && renderdoc_api->api_1_0_0()->IsFrameCapturing()) {
|
||||
renderdoc_api->api_1_0_0()->EndFrameCapture(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ std::vector<std::unique_ptr<hid::InputDriver>> HidDemoApp::CreateInputDrivers(
|
|||
|
||||
bool HidDemoApp::OnInitialize() {
|
||||
// Create the graphics provider that provides the presenter for the window.
|
||||
graphics_provider_ = xe::ui::vulkan::VulkanProvider::Create(true);
|
||||
graphics_provider_ = xe::ui::vulkan::VulkanProvider::Create(false, true);
|
||||
if (!graphics_provider_) {
|
||||
XELOGE("Failed to initialize the graphics provider");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
#include "xenia/ui/graphics_provider.h"
|
||||
|
||||
#define XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES 1
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace d3d12 {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ class GraphicsProvider {
|
|||
kQualcomm = 0x5143,
|
||||
};
|
||||
|
||||
GraphicsProvider(const GraphicsProvider&) = delete;
|
||||
GraphicsProvider& operator=(const GraphicsProvider&) = delete;
|
||||
GraphicsProvider(GraphicsProvider&&) = delete;
|
||||
GraphicsProvider& operator=(GraphicsProvider&&) = delete;
|
||||
|
||||
virtual ~GraphicsProvider() = default;
|
||||
|
||||
// It's safe to reinitialize the presenter in the host GPU loss callback if it
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "xenia/ui/renderdoc_api.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
|
|
@ -22,54 +21,57 @@
|
|||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
bool RenderdocApi::Initialize() {
|
||||
Shutdown();
|
||||
std::unique_ptr<RenderDocAPI> RenderDocAPI::CreateIfConnected() {
|
||||
std::unique_ptr<RenderDocAPI> renderdoc_api(new RenderDocAPI());
|
||||
|
||||
pRENDERDOC_GetAPI get_api = nullptr;
|
||||
// The RenderDoc library should be already loaded into the process if
|
||||
|
||||
// The RenderDoc library should already be loaded into the process if
|
||||
// RenderDoc is attached - this is why RTLD_NOLOAD or GetModuleHandle instead
|
||||
// of LoadLibrary.
|
||||
#if XE_PLATFORM_LINUX
|
||||
#if XE_PLATFORM_ANDROID
|
||||
const char* librenderdoc_name = "libVkLayer_GLES_RenderDoc.so";
|
||||
const char* const library_name = "libVkLayer_GLES_RenderDoc.so";
|
||||
#else
|
||||
const char* librenderdoc_name = "librenderdoc.so";
|
||||
const char* const library_name = "librenderdoc.so";
|
||||
#endif
|
||||
library_ = dlopen(librenderdoc_name, RTLD_NOW | RTLD_NOLOAD);
|
||||
if (library_) {
|
||||
get_api = pRENDERDOC_GetAPI(dlsym(library_, "RENDERDOC_GetAPI"));
|
||||
renderdoc_api->library_ = dlopen(library_name, RTLD_NOW | RTLD_NOLOAD);
|
||||
if (!renderdoc_api->library_) {
|
||||
return nullptr;
|
||||
}
|
||||
get_api =
|
||||
pRENDERDOC_GetAPI(dlsym(renderdoc_api->library_, "RENDERDOC_GetAPI"));
|
||||
#elif XE_PLATFORM_WIN32
|
||||
library_ = GetModuleHandleA("renderdoc.dll");
|
||||
if (library_) {
|
||||
renderdoc_api->library_ = GetModuleHandleW(L"renderdoc.dll");
|
||||
if (!renderdoc_api->library_) {
|
||||
return nullptr;
|
||||
}
|
||||
get_api = pRENDERDOC_GetAPI(
|
||||
GetProcAddress(HMODULE(library_), "RENDERDOC_GetAPI"));
|
||||
}
|
||||
GetProcAddress(renderdoc_api->library_, "RENDERDOC_GetAPI"));
|
||||
#endif
|
||||
if (!get_api) {
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
// get_api will be null if RenderDoc is not attached, or the API isn't
|
||||
|
||||
// get_api will be null if RenderDoc is not connected, or the API isn't
|
||||
// available on this platform, or there was an error.
|
||||
if (!get_api || !get_api(eRENDERDOC_API_Version_1_0_0, (void**)&api_1_0_0_) ||
|
||||
!api_1_0_0_) {
|
||||
Shutdown();
|
||||
return false;
|
||||
if (!get_api ||
|
||||
!get_api(eRENDERDOC_API_Version_1_0_0,
|
||||
(void**)&renderdoc_api->api_1_0_0_) ||
|
||||
!renderdoc_api->api_1_0_0_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XELOGI("RenderDoc API initialized");
|
||||
return true;
|
||||
|
||||
return renderdoc_api;
|
||||
}
|
||||
|
||||
void RenderdocApi::Shutdown() {
|
||||
api_1_0_0_ = nullptr;
|
||||
if (library_) {
|
||||
RenderDocAPI::~RenderDocAPI() {
|
||||
#if XE_PLATFORM_LINUX
|
||||
if (library_) {
|
||||
dlclose(library_);
|
||||
}
|
||||
#endif
|
||||
// Not calling FreeLibrary on Windows as GetModuleHandle doesn't increment
|
||||
// the reference count.
|
||||
library_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
|||
|
|
@ -10,26 +10,39 @@
|
|||
#ifndef XENIA_UI_RENDERDOC_API_H_
|
||||
#define XENIA_UI_RENDERDOC_API_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "third_party/renderdoc/renderdoc_app.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
class RenderdocApi {
|
||||
class RenderDocAPI {
|
||||
public:
|
||||
RenderdocApi() = default;
|
||||
RenderdocApi(const RenderdocApi& renderdoc_api) = delete;
|
||||
RenderdocApi& operator=(const RenderdocApi& renderdoc_api) = delete;
|
||||
~RenderdocApi() { Shutdown(); }
|
||||
static std::unique_ptr<RenderDocAPI> CreateIfConnected();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
RenderDocAPI(const RenderDocAPI&) = delete;
|
||||
RenderDocAPI& operator=(const RenderDocAPI&) = delete;
|
||||
|
||||
// nullptr if not attached.
|
||||
~RenderDocAPI();
|
||||
|
||||
// Always present if this object exists.
|
||||
const RENDERDOC_API_1_0_0* api_1_0_0() const { return api_1_0_0_; }
|
||||
|
||||
private:
|
||||
explicit RenderDocAPI() = default;
|
||||
|
||||
#if XE_PLATFORM_LINUX
|
||||
void* library_ = nullptr;
|
||||
#elif XE_PLATFORM_WIN32
|
||||
HMODULE library_ = nullptr;
|
||||
#endif
|
||||
|
||||
const RENDERDOC_API_1_0_0* api_1_0_0_ = nullptr;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ namespace ui {
|
|||
namespace vulkan {
|
||||
|
||||
void LinkedTypeDescriptorSetAllocator::Reset() {
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyDescriptorPool, device,
|
||||
page_usable_latest_.pool);
|
||||
page_usable_latest_.descriptors_remaining.reset();
|
||||
|
|
@ -53,8 +53,8 @@ VkDescriptorSet LinkedTypeDescriptorSetAllocator::Allocate(
|
|||
}
|
||||
#endif
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkDescriptorSetAllocateInfo descriptor_set_allocate_info;
|
||||
descriptor_set_allocate_info.sType =
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
|
@ -54,13 +54,15 @@ class LinkedTypeDescriptorSetAllocator {
|
|||
// Multiple descriptor sizes for the same descriptor type, and zero sizes, are
|
||||
// not allowed.
|
||||
explicit LinkedTypeDescriptorSetAllocator(
|
||||
const ui::vulkan::VulkanProvider& provider,
|
||||
const VkDescriptorPoolSize* descriptor_sizes,
|
||||
uint32_t descriptor_size_count, uint32_t descriptor_sets_per_page)
|
||||
: provider_(provider),
|
||||
const VulkanDevice* const vulkan_device,
|
||||
const VkDescriptorPoolSize* const descriptor_sizes,
|
||||
const uint32_t descriptor_size_count,
|
||||
const uint32_t descriptor_sets_per_page)
|
||||
: vulkan_device_(vulkan_device),
|
||||
descriptor_pool_sizes_(new VkDescriptorPoolSize[descriptor_size_count]),
|
||||
descriptor_pool_size_count_(descriptor_size_count),
|
||||
descriptor_sets_per_page_(descriptor_sets_per_page) {
|
||||
assert_not_null(vulkan_device);
|
||||
assert_not_zero(descriptor_size_count);
|
||||
assert_not_zero(descriptor_sets_per_page_);
|
||||
#ifndef NDEBUG
|
||||
|
|
@ -94,7 +96,7 @@ class LinkedTypeDescriptorSetAllocator {
|
|||
uint32_t descriptor_sets_remaining;
|
||||
};
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
|
||||
std::unique_ptr<VkDescriptorPoolSize[]> descriptor_pool_sizes_;
|
||||
uint32_t descriptor_pool_size_count_;
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@ namespace ui {
|
|||
namespace vulkan {
|
||||
|
||||
SingleLayoutDescriptorSetPool::SingleLayoutDescriptorSetPool(
|
||||
const VulkanProvider& provider, uint32_t pool_set_count,
|
||||
uint32_t set_layout_descriptor_counts_count,
|
||||
const VkDescriptorPoolSize* set_layout_descriptor_counts,
|
||||
VkDescriptorSetLayout set_layout)
|
||||
: provider_(provider),
|
||||
const VulkanDevice* const vulkan_device, const uint32_t pool_set_count,
|
||||
const uint32_t set_layout_descriptor_counts_count,
|
||||
const VkDescriptorPoolSize* const set_layout_descriptor_counts,
|
||||
const VkDescriptorSetLayout set_layout)
|
||||
: vulkan_device_(vulkan_device),
|
||||
pool_set_count_(pool_set_count),
|
||||
set_layout_(set_layout) {
|
||||
assert_not_null(vulkan_device);
|
||||
assert_not_zero(pool_set_count);
|
||||
pool_descriptor_counts_.resize(set_layout_descriptor_counts_count);
|
||||
for (uint32_t i = 0; i < set_layout_descriptor_counts_count; ++i) {
|
||||
|
|
@ -38,8 +39,8 @@ SingleLayoutDescriptorSetPool::SingleLayoutDescriptorSetPool(
|
|||
}
|
||||
|
||||
SingleLayoutDescriptorSetPool::~SingleLayoutDescriptorSetPool() {
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
if (current_pool_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyDescriptorPool(device, current_pool_, nullptr);
|
||||
}
|
||||
|
|
@ -55,8 +56,8 @@ size_t SingleLayoutDescriptorSetPool::Allocate() {
|
|||
return free_index;
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
// Two iterations so if vkAllocateDescriptorSets fails even with a non-zero
|
||||
// current_pool_sets_remaining_, another attempt will be made in a new pool.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
|
@ -28,7 +28,7 @@ class SingleLayoutDescriptorSetPool {
|
|||
// set count will be done internally). The descriptor set layout must not be
|
||||
// destroyed until this object is also destroyed.
|
||||
SingleLayoutDescriptorSetPool(
|
||||
const VulkanProvider& provider, uint32_t pool_set_count,
|
||||
const VulkanDevice* vulkan_device, uint32_t pool_set_count,
|
||||
uint32_t set_layout_descriptor_counts_count,
|
||||
const VkDescriptorPoolSize* set_layout_descriptor_counts,
|
||||
VkDescriptorSetLayout set_layout);
|
||||
|
|
@ -43,7 +43,7 @@ class SingleLayoutDescriptorSetPool {
|
|||
VkDescriptorSet Get(size_t index) const { return descriptor_sets_[index]; }
|
||||
|
||||
private:
|
||||
const VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
uint32_t pool_set_count_;
|
||||
std::vector<VkDescriptorPoolSize> pool_descriptor_counts_;
|
||||
VkDescriptorSetLayout set_layout_;
|
||||
|
|
|
|||
88
src/xenia/ui/vulkan/ui_samplers.cc
Normal file
88
src/xenia/ui/vulkan/ui_samplers.cc
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/vulkan/ui_samplers.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
std::unique_ptr<UISamplers> UISamplers::Create(
|
||||
const VulkanDevice* const vulkan_device) {
|
||||
assert_not_null(vulkan_device);
|
||||
|
||||
std::unique_ptr<UISamplers> ui_samplers(new UISamplers(vulkan_device));
|
||||
|
||||
const VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
VkSamplerCreateInfo sampler_create_info = {
|
||||
VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
|
||||
|
||||
for (int sampler_index = 0; sampler_index < kSamplerCount; ++sampler_index) {
|
||||
if (sampler_index == kSamplerIndexLinearRepeat ||
|
||||
sampler_index == kSamplerIndexLinearClampToEdge) {
|
||||
sampler_create_info.magFilter = VK_FILTER_LINEAR;
|
||||
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
} else {
|
||||
sampler_create_info.magFilter = VK_FILTER_NEAREST;
|
||||
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
}
|
||||
sampler_create_info.minFilter = sampler_create_info.magFilter;
|
||||
|
||||
if (sampler_index == kSamplerIndexNearestClampToEdge ||
|
||||
sampler_index == kSamplerIndexLinearClampToEdge) {
|
||||
sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
} else {
|
||||
sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
}
|
||||
sampler_create_info.addressModeV = sampler_create_info.addressModeU;
|
||||
sampler_create_info.addressModeW = sampler_create_info.addressModeU;
|
||||
|
||||
const VkResult sampler_create_result =
|
||||
dfn.vkCreateSampler(device, &sampler_create_info, nullptr,
|
||||
&ui_samplers->samplers_[sampler_index]);
|
||||
if (sampler_create_result != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"Failed to create the Vulkan UI sampler with filter {}, addressing "
|
||||
"mode {}: {}",
|
||||
vk::to_string(vk::Filter(sampler_create_info.magFilter)),
|
||||
vk::to_string(
|
||||
vk::SamplerAddressMode(sampler_create_info.addressModeU)),
|
||||
vk::to_string(vk::Result(sampler_create_result)));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ui_samplers;
|
||||
}
|
||||
|
||||
UISamplers::~UISamplers() {
|
||||
for (const VkSampler sampler : samplers_) {
|
||||
if (sampler == VK_NULL_HANDLE) {
|
||||
continue;
|
||||
}
|
||||
vulkan_device_->functions().vkDestroySampler(vulkan_device_->device(),
|
||||
sampler, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
UISamplers::UISamplers(const VulkanDevice* vulkan_device)
|
||||
: vulkan_device_(vulkan_device) {
|
||||
assert_not_null(vulkan_device);
|
||||
|
||||
samplers_.fill(VK_NULL_HANDLE);
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
63
src/xenia/ui/vulkan/ui_samplers.h
Normal file
63
src/xenia/ui/vulkan/ui_samplers.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_VULKAN_VULKAN_UI_SAMPLERS_H_
|
||||
#define XENIA_UI_VULKAN_VULKAN_UI_SAMPLERS_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
/// Samplers that can be used for presentation and UI drawing.
|
||||
/// Because maxSamplerAllocationCount can be as low as 4000 (on Nvidia and Intel
|
||||
/// GPUs primarily), no other samplers must be created for UI purposes.
|
||||
/// The rest of the sampler allocation space on the device must be available to
|
||||
/// GPU emulation.
|
||||
class UISamplers {
|
||||
public:
|
||||
static std::unique_ptr<UISamplers> Create(const VulkanDevice* vulkan_device);
|
||||
|
||||
UISamplers(const UISamplers&) = delete;
|
||||
UISamplers& operator=(const UISamplers&) = delete;
|
||||
UISamplers(UISamplers&&) = delete;
|
||||
UISamplers& operator=(UISamplers&&) = delete;
|
||||
|
||||
~UISamplers();
|
||||
|
||||
enum SamplerIndex {
|
||||
kSamplerIndexNearestRepeat,
|
||||
kSamplerIndexNearestClampToEdge,
|
||||
kSamplerIndexLinearRepeat,
|
||||
kSamplerIndexLinearClampToEdge,
|
||||
|
||||
kSamplerCount,
|
||||
};
|
||||
|
||||
const std::array<VkSampler, kSamplerCount>& samplers() const {
|
||||
return samplers_;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit UISamplers(const VulkanDevice* vulkan_device);
|
||||
|
||||
const VulkanDevice* vulkan_device_;
|
||||
|
||||
std::array<VkSampler, kSamplerCount> samplers_;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_VULKAN_VULKAN_UI_SAMPLERS_H_
|
||||
50
src/xenia/ui/vulkan/vulkan_api.h
Normal file
50
src/xenia/ui/vulkan/vulkan_api.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_VULKAN_VULKAN_API_H_
|
||||
#define XENIA_UI_VULKAN_VULKAN_API_H_
|
||||
|
||||
#include "xenia/base/assert.h" // For Vulkan-Hpp.
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#ifndef VK_NO_PROTOTYPES
|
||||
#define VK_NO_PROTOTYPES
|
||||
#endif
|
||||
|
||||
#ifndef VK_ENABLE_BETA_EXTENSIONS
|
||||
#define VK_ENABLE_BETA_EXTENSIONS
|
||||
#endif
|
||||
|
||||
#if XE_PLATFORM_ANDROID
|
||||
#ifndef VK_USE_PLATFORM_ANDROID_KHR
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if XE_PLATFORM_GNU_LINUX
|
||||
#ifndef VK_USE_PLATFORM_XCB_KHR
|
||||
#define VK_USE_PLATFORM_XCB_KHR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
// Must be included before including vulkan.h with VK_USE_PLATFORM_WIN32_KHR
|
||||
// because it includes Windows.h too.
|
||||
#include "xenia/base/platform_win.h"
|
||||
#ifndef VK_USE_PLATFORM_WIN32_KHR
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "third_party/Vulkan-Headers/include/vulkan/vulkan.h"
|
||||
|
||||
#include "third_party/Vulkan-Headers/include/vulkan/vulkan_hpp_macros.hpp"
|
||||
#include "third_party/Vulkan-Headers/include/vulkan/vulkan_to_string.hpp"
|
||||
|
||||
#endif // XENIA_UI_VULKAN_VULKAN_API_H_
|
||||
828
src/xenia/ui/vulkan/vulkan_device.cc
Normal file
828
src/xenia/ui/vulkan/vulkan_device.cc
Normal file
|
|
@ -0,0 +1,828 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
template <typename Structure, VkStructureType StructureType>
|
||||
struct VulkanFeatures {
|
||||
Structure supported = {StructureType};
|
||||
Structure enabled = {StructureType};
|
||||
|
||||
void Link(VkPhysicalDeviceFeatures2& supported_features_2,
|
||||
VkDeviceCreateInfo& device_create_info) {
|
||||
supported.pNext = supported_features_2.pNext;
|
||||
supported_features_2.pNext = &supported;
|
||||
enabled.pNext = const_cast<void*>(device_create_info.pNext);
|
||||
device_create_info.pNext = &enabled;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<VulkanDevice> VulkanDevice::CreateIfSupported(
|
||||
const VulkanInstance* const vulkan_instance,
|
||||
const VkPhysicalDevice physical_device, const bool with_gpu_emulation,
|
||||
const bool with_swapchain) {
|
||||
assert_not_null(vulkan_instance);
|
||||
assert_not_null(physical_device);
|
||||
|
||||
const VulkanInstance::Functions& ifn = vulkan_instance->functions();
|
||||
|
||||
// Get supported Vulkan 1.0 properties and features.
|
||||
|
||||
VkPhysicalDeviceProperties properties = {};
|
||||
ifn.vkGetPhysicalDeviceProperties(physical_device, &properties);
|
||||
|
||||
const uint32_t unclamped_api_version = properties.apiVersion;
|
||||
if (vulkan_instance->api_version() < VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
// From the VkApplicationInfo specification:
|
||||
//
|
||||
// "The Khronos validation layers will treat apiVersion as the highest API
|
||||
// version the application targets, and will validate API usage against the
|
||||
// minimum of that version and the implementation version (instance or
|
||||
// device, depending on context). If an application tries to use
|
||||
// functionality from a greater version than this, a validation error will
|
||||
// be triggered."
|
||||
//
|
||||
// "Vulkan 1.0 implementations were required to return
|
||||
// VK_ERROR_INCOMPATIBLE_DRIVER if apiVersion was larger than 1.0."
|
||||
properties.apiVersion = VK_MAKE_API_VERSION(
|
||||
0, 1, 0, VK_API_VERSION_PATCH(properties.apiVersion));
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures supported_features = {};
|
||||
ifn.vkGetPhysicalDeviceFeatures(physical_device, &supported_features);
|
||||
|
||||
if (with_gpu_emulation) {
|
||||
if (!supported_features.independentBlend) {
|
||||
// Not trivial to work around:
|
||||
// - Affects not only the blend equation, but also the color write mask.
|
||||
// - Can't reuse the blend state of the first attachment for all because
|
||||
// some attachments may have a format that doesn't support blending.
|
||||
// - Not possible to split the draw into per-attachment draws because of
|
||||
// depth / stencil.
|
||||
// Not supported only on the proprietary driver for the Qualcomm
|
||||
// Adreno 4xx, where the driver is largely experimental and doesn't expose
|
||||
// a lot of the functionality available in the hardware.
|
||||
XELOGW(
|
||||
"Vulkan device '{}' doesn't support the independentBlend feature "
|
||||
"required for GPU emulation",
|
||||
properties.deviceName);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable needed extensions.
|
||||
|
||||
std::unique_ptr<VulkanDevice> device(
|
||||
new VulkanDevice(vulkan_instance, physical_device));
|
||||
|
||||
const bool get_physical_device_properties2_supported =
|
||||
vulkan_instance->extensions().ext_1_1_KHR_get_physical_device_properties2;
|
||||
|
||||
// Name pointers from `requested_extensions` will be used in the enabled
|
||||
// extensions vector.
|
||||
std::unordered_map<std::string, bool*> requested_extensions;
|
||||
|
||||
const auto request_promoted_extension =
|
||||
[&](const char* const name, uint32_t const major, uint32_t const minor,
|
||||
bool* const supported_ptr) {
|
||||
assert_not_null(supported_ptr);
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, major, minor, 0)) {
|
||||
*supported_ptr = true;
|
||||
} else {
|
||||
requested_extensions.emplace(name, supported_ptr);
|
||||
}
|
||||
};
|
||||
|
||||
#define XE_UI_VULKAN_STRUCT_EXTENSION(name) \
|
||||
requested_extensions.emplace("VK_" #name, &device->extensions_.ext_##name);
|
||||
#define XE_UI_VULKAN_LOCAL_EXTENSION(name) \
|
||||
requested_extensions.emplace("VK_" #name, &ext_##name);
|
||||
#define XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(name, major, minor) \
|
||||
request_promoted_extension( \
|
||||
"VK_" #name, major, minor, \
|
||||
&device->extensions_.ext_##major##_##minor##_##name);
|
||||
#define XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(name, major, minor) \
|
||||
request_promoted_extension("VK_" #name, major, minor, \
|
||||
&ext_##major##_##minor##_##name);
|
||||
|
||||
bool ext_KHR_portability_subset = false;
|
||||
bool ext_1_2_KHR_driver_properties = false;
|
||||
if (get_physical_device_properties2_supported) {
|
||||
// #164. Must be enabled according to the specification if the physical
|
||||
// device is a portability subset one.
|
||||
XE_UI_VULKAN_LOCAL_EXTENSION(KHR_portability_subset)
|
||||
// #197
|
||||
XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(KHR_driver_properties, 1, 2)
|
||||
}
|
||||
|
||||
// Used by the Vulkan Memory Allocator and potentially by Xenia.
|
||||
// #128.
|
||||
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_dedicated_allocation, 1, 1)
|
||||
// #147. Also must be enabled for VK_KHR_dedicated_allocation and
|
||||
// VK_KHR_sampler_ycbcr_conversion.
|
||||
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_get_memory_requirements2, 1, 1)
|
||||
// #158. Also must be enabled for VK_KHR_sampler_ycbcr_conversion.
|
||||
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_bind_memory2, 1, 1)
|
||||
if (get_physical_device_properties2_supported) {
|
||||
// #238.
|
||||
XE_UI_VULKAN_STRUCT_EXTENSION(EXT_memory_budget)
|
||||
}
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
// #414.
|
||||
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_maintenance4, 1, 3)
|
||||
}
|
||||
|
||||
if (with_swapchain) {
|
||||
// #2.
|
||||
XE_UI_VULKAN_STRUCT_EXTENSION(KHR_swapchain)
|
||||
}
|
||||
|
||||
bool ext_1_2_KHR_sampler_mirror_clamp_to_edge = false;
|
||||
bool ext_1_1_KHR_maintenance1 = false;
|
||||
bool ext_1_2_KHR_shader_float_controls = false;
|
||||
bool ext_EXT_fragment_shader_interlock = false;
|
||||
bool ext_1_3_EXT_shader_demote_to_helper_invocation = false;
|
||||
bool ext_EXT_non_seamless_cube_map = false;
|
||||
if (with_gpu_emulation) {
|
||||
// #15.
|
||||
XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(KHR_sampler_mirror_clamp_to_edge, 1,
|
||||
2)
|
||||
// #70. Must be enabled for VK_KHR_sampler_ycbcr_conversion.
|
||||
XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(KHR_maintenance1, 1, 1)
|
||||
// #141.
|
||||
XE_UI_VULKAN_STRUCT_EXTENSION(EXT_shader_stencil_export)
|
||||
// #148.
|
||||
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_image_format_list, 1, 2)
|
||||
if (get_physical_device_properties2_supported) {
|
||||
// #157.
|
||||
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_sampler_ycbcr_conversion, 1, 1)
|
||||
// #198. Also must be enabled for VK_KHR_spirv_1_4.
|
||||
XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(KHR_shader_float_controls, 1, 2)
|
||||
// #252.
|
||||
XE_UI_VULKAN_LOCAL_EXTENSION(EXT_fragment_shader_interlock)
|
||||
// #277.
|
||||
XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION(
|
||||
EXT_shader_demote_to_helper_invocation, 1, 3)
|
||||
// #423.
|
||||
XE_UI_VULKAN_LOCAL_EXTENSION(EXT_non_seamless_cube_map)
|
||||
}
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
// #237.
|
||||
XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION(KHR_spirv_1_4, 1, 2)
|
||||
}
|
||||
}
|
||||
|
||||
#undef XE_UI_VULKAN_STRUCT_EXTENSION
|
||||
#undef XE_UI_VULKAN_LOCAL_EXTENSION
|
||||
#undef XE_UI_VULKAN_STRUCT_PROMOTED_EXTENSION
|
||||
#undef XE_UI_VULKAN_LOCAL_PROMOTED_EXTENSION
|
||||
|
||||
std::vector<const char*> enabled_extensions;
|
||||
{
|
||||
uint32_t supported_extension_count = 0;
|
||||
const VkResult get_supported_extension_count_result =
|
||||
ifn.vkEnumerateDeviceExtensionProperties(
|
||||
physical_device, nullptr, &supported_extension_count, nullptr);
|
||||
if (get_supported_extension_count_result != VK_SUCCESS &&
|
||||
get_supported_extension_count_result != VK_INCOMPLETE) {
|
||||
XELOGW("Failed to get the Vulkan device '{}' extension count",
|
||||
properties.deviceName);
|
||||
return nullptr;
|
||||
}
|
||||
if (supported_extension_count) {
|
||||
std::vector<VkExtensionProperties> supported_extensions(
|
||||
supported_extension_count);
|
||||
if (ifn.vkEnumerateDeviceExtensionProperties(
|
||||
physical_device, nullptr, &supported_extension_count,
|
||||
supported_extensions.data()) != VK_SUCCESS) {
|
||||
XELOGW("Failed to get the Vulkan device '{}' extensions",
|
||||
properties.deviceName);
|
||||
return nullptr;
|
||||
}
|
||||
assert_true(supported_extension_count == supported_extensions.size());
|
||||
for (const VkExtensionProperties& supported_extension :
|
||||
supported_extensions) {
|
||||
const auto requested_extension_it =
|
||||
requested_extensions.find(supported_extension.extensionName);
|
||||
if (requested_extension_it == requested_extensions.cend()) {
|
||||
continue;
|
||||
}
|
||||
assert_not_null(requested_extension_it->second);
|
||||
if (!*requested_extension_it->second) {
|
||||
enabled_extensions.emplace_back(
|
||||
requested_extension_it->first.c_str());
|
||||
*requested_extension_it->second = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (with_swapchain && !device->extensions_.ext_KHR_swapchain) {
|
||||
XELOGW("Vulkan device '{}' doesn't support swapchains",
|
||||
properties.deviceName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VkDeviceCreateInfo device_create_info = {
|
||||
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
|
||||
|
||||
device_create_info.enabledExtensionCount =
|
||||
uint32_t(enabled_extensions.size());
|
||||
device_create_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
|
||||
// Get supported Vulkan 1.1+ and extension properties and features.
|
||||
//
|
||||
// The property and feature structures are initialized to zero or to the
|
||||
// minimum / maximum requirements for the simplicity of handling unavailable
|
||||
// VK_KHR_get_physical_device_properties2.
|
||||
|
||||
VkPhysicalDeviceProperties2 properties_2 = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
|
||||
|
||||
VkPhysicalDeviceFeatures2 supported_features_2 = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
|
||||
|
||||
VulkanFeatures<VkPhysicalDeviceVulkan12Features,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES>
|
||||
features_1_2;
|
||||
VulkanFeatures<VkPhysicalDeviceVulkan13Features,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES>
|
||||
features_1_3;
|
||||
VulkanFeatures<
|
||||
VkPhysicalDevicePortabilitySubsetFeaturesKHR,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR>
|
||||
features_KHR_portability_subset;
|
||||
VkPhysicalDeviceDriverPropertiesKHR properties_1_2_KHR_driver_properties = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES};
|
||||
VkPhysicalDeviceFloatControlsProperties
|
||||
properties_1_2_KHR_shader_float_controls = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES};
|
||||
VulkanFeatures<
|
||||
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT>
|
||||
features_EXT_fragment_shader_interlock;
|
||||
VulkanFeatures<
|
||||
VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT>
|
||||
features_1_3_EXT_shader_demote_to_helper_invocation;
|
||||
VulkanFeatures<
|
||||
VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT>
|
||||
features_EXT_non_seamless_cube_map;
|
||||
|
||||
if (get_physical_device_properties2_supported) {
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) {
|
||||
features_1_2.Link(supported_features_2, device_create_info);
|
||||
}
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
|
||||
features_1_3.Link(supported_features_2, device_create_info);
|
||||
} else {
|
||||
if (ext_1_3_EXT_shader_demote_to_helper_invocation) {
|
||||
features_1_3_EXT_shader_demote_to_helper_invocation.Link(
|
||||
supported_features_2, device_create_info);
|
||||
}
|
||||
}
|
||||
if (ext_KHR_portability_subset) {
|
||||
features_KHR_portability_subset.Link(supported_features_2,
|
||||
device_create_info);
|
||||
}
|
||||
if (ext_1_2_KHR_driver_properties) {
|
||||
properties_1_2_KHR_driver_properties.pNext = properties_2.pNext;
|
||||
properties_2.pNext = &properties_1_2_KHR_driver_properties;
|
||||
}
|
||||
if (ext_1_2_KHR_shader_float_controls) {
|
||||
properties_1_2_KHR_shader_float_controls.pNext = properties_2.pNext;
|
||||
properties_2.pNext = &properties_1_2_KHR_shader_float_controls;
|
||||
}
|
||||
if (ext_EXT_fragment_shader_interlock) {
|
||||
features_EXT_fragment_shader_interlock.Link(supported_features_2,
|
||||
device_create_info);
|
||||
}
|
||||
if (ext_EXT_non_seamless_cube_map) {
|
||||
features_EXT_non_seamless_cube_map.Link(supported_features_2,
|
||||
device_create_info);
|
||||
}
|
||||
ifn.vkGetPhysicalDeviceProperties2(physical_device, &properties_2);
|
||||
ifn.vkGetPhysicalDeviceFeatures2(physical_device, &supported_features_2);
|
||||
}
|
||||
|
||||
uint32_t queue_family_count = 0;
|
||||
ifn.vkGetPhysicalDeviceQueueFamilyProperties(physical_device,
|
||||
&queue_family_count, nullptr);
|
||||
std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
|
||||
ifn.vkGetPhysicalDeviceQueueFamilyProperties(
|
||||
physical_device, &queue_family_count, queue_families.data());
|
||||
|
||||
device->queue_families_.resize(queue_family_count);
|
||||
|
||||
uint32_t first_queue_family_graphics_compute_sparse_binding = UINT32_MAX;
|
||||
uint32_t first_queue_family_graphics_compute = UINT32_MAX;
|
||||
uint32_t first_queue_family_sparse_binding = UINT32_MAX;
|
||||
bool has_presentation_queue_family = false;
|
||||
|
||||
for (uint32_t queue_family_index = 0; queue_family_index < queue_family_count;
|
||||
++queue_family_index) {
|
||||
QueueFamily& queue_family = device->queue_families_[queue_family_index];
|
||||
const VkQueueFamilyProperties& queue_family_properties =
|
||||
queue_families[queue_family_index];
|
||||
|
||||
const VkQueueFlags queue_unsupported_flags =
|
||||
~queue_family_properties.queueFlags;
|
||||
|
||||
if (!(queue_unsupported_flags &
|
||||
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT))) {
|
||||
first_queue_family_graphics_compute =
|
||||
std::min(queue_family_index, first_queue_family_graphics_compute);
|
||||
}
|
||||
|
||||
if (with_gpu_emulation && supported_features.sparseBinding &&
|
||||
!(queue_unsupported_flags & VK_QUEUE_SPARSE_BINDING_BIT)) {
|
||||
first_queue_family_sparse_binding =
|
||||
std::min(queue_family_index, first_queue_family_sparse_binding);
|
||||
if (!(queue_unsupported_flags &
|
||||
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT))) {
|
||||
first_queue_family_graphics_compute_sparse_binding =
|
||||
std::min(queue_family_index,
|
||||
first_queue_family_graphics_compute_sparse_binding);
|
||||
}
|
||||
}
|
||||
|
||||
if (with_swapchain) {
|
||||
#if XE_PLATFORM_WIN32
|
||||
queue_family.may_support_presentation =
|
||||
vulkan_instance->extensions().ext_KHR_win32_surface &&
|
||||
ifn.vkGetPhysicalDeviceWin32PresentationSupportKHR(
|
||||
physical_device, queue_family_index);
|
||||
#else
|
||||
queue_family.may_support_presentation = true;
|
||||
#endif
|
||||
if (queue_family.may_support_presentation) {
|
||||
queue_family.queues.resize(
|
||||
std::max(size_t(1), queue_family.queues.size()));
|
||||
has_presentation_queue_family = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_queue_family_graphics_compute == UINT32_MAX) {
|
||||
// Not valid according to the Vulkan specification, but for safety.
|
||||
XELOGW(
|
||||
"Vulkan device '{}' doesn't provide a graphics and compute queue "
|
||||
"family",
|
||||
properties.deviceName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (with_swapchain && !has_presentation_queue_family) {
|
||||
XELOGW(
|
||||
"Vulkan device '{}' doesn't provide a queue family that supports "
|
||||
"presentation",
|
||||
properties.deviceName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the queues to create.
|
||||
|
||||
if (first_queue_family_sparse_binding == UINT32_MAX) {
|
||||
// Not valid not to provide a sparse binding queue if the sparseBinding
|
||||
// feature is supported according to the Vulkan specification, but for
|
||||
// safety and simplicity.
|
||||
supported_features.sparseBinding = VK_FALSE;
|
||||
}
|
||||
if (!supported_features.sparseBinding) {
|
||||
supported_features.sparseResidencyBuffer = VK_FALSE;
|
||||
supported_features.sparseResidencyImage2D = VK_FALSE;
|
||||
supported_features.sparseResidencyImage3D = VK_FALSE;
|
||||
supported_features.sparseResidency2Samples = VK_FALSE;
|
||||
supported_features.sparseResidency4Samples = VK_FALSE;
|
||||
supported_features.sparseResidency8Samples = VK_FALSE;
|
||||
supported_features.sparseResidency16Samples = VK_FALSE;
|
||||
supported_features.sparseResidencyAliased = VK_FALSE;
|
||||
}
|
||||
|
||||
// Prefer using one queue for everything whenever possible for simplicity.
|
||||
// TODO(Triang3l): Research if separate queues for purposes like composition,
|
||||
// swapchain image presentation, and sparse binding, may be beneficial.
|
||||
|
||||
if (first_queue_family_graphics_compute_sparse_binding != UINT32_MAX) {
|
||||
device->queue_family_graphics_compute_ =
|
||||
first_queue_family_graphics_compute_sparse_binding;
|
||||
device->queue_family_sparse_binding_ =
|
||||
first_queue_family_graphics_compute_sparse_binding;
|
||||
} else {
|
||||
device->queue_family_graphics_compute_ =
|
||||
first_queue_family_graphics_compute;
|
||||
device->queue_family_sparse_binding_ = first_queue_family_sparse_binding;
|
||||
}
|
||||
|
||||
device->queue_families_[device->queue_family_graphics_compute_].queues.resize(
|
||||
std::max(size_t(1),
|
||||
device->queue_families_[device->queue_family_graphics_compute_]
|
||||
.queues.size()));
|
||||
if (device->queue_family_sparse_binding_ != UINT32_MAX) {
|
||||
device->queue_families_[device->queue_family_sparse_binding_].queues.resize(
|
||||
std::max(size_t(1),
|
||||
device->queue_families_[device->queue_family_sparse_binding_]
|
||||
.queues.size()));
|
||||
}
|
||||
|
||||
size_t max_enabled_queues_per_family = 0;
|
||||
for (const QueueFamily& queue_family : device->queue_families_) {
|
||||
max_enabled_queues_per_family =
|
||||
std::max(queue_family.queues.size(), max_enabled_queues_per_family);
|
||||
}
|
||||
const std::vector<float> queue_priorities(max_enabled_queues_per_family,
|
||||
1.0f);
|
||||
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
|
||||
for (size_t queue_family_index = 0;
|
||||
queue_family_index < device->queue_families_.size();
|
||||
++queue_family_index) {
|
||||
const QueueFamily& queue_family =
|
||||
device->queue_families_[queue_family_index];
|
||||
if (queue_family.queues.empty()) {
|
||||
continue;
|
||||
}
|
||||
VkDeviceQueueCreateInfo& queue_create_info =
|
||||
queue_create_infos.emplace_back();
|
||||
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
queue_create_info.pNext = nullptr;
|
||||
queue_create_info.flags = 0;
|
||||
queue_create_info.queueFamilyIndex = uint32_t(queue_family_index);
|
||||
queue_create_info.queueCount = uint32_t(queue_family.queues.size());
|
||||
queue_create_info.pQueuePriorities = queue_priorities.data();
|
||||
}
|
||||
device_create_info.queueCreateInfoCount = uint32_t(queue_create_infos.size());
|
||||
device_create_info.pQueueCreateInfos = queue_create_infos.data();
|
||||
|
||||
// Enable needed features and copy the properties.
|
||||
//
|
||||
// Enabling only actually used features because drivers may take more optimal
|
||||
// paths when certain features are disabled. Also, in VK_EXT_shader_object,
|
||||
// the state that the application must set for the draw depends on which
|
||||
// features are enabled.
|
||||
|
||||
device->properties_.apiVersion = properties.apiVersion;
|
||||
device->properties_.driverVersion = properties.driverVersion;
|
||||
device->properties_.vendorID = properties.vendorID;
|
||||
device->properties_.deviceID = properties.deviceID;
|
||||
std::strcpy(device->properties_.deviceName, properties.deviceName);
|
||||
|
||||
XELOGI(
|
||||
"Vulkan device '{}': API {}.{}.{}, vendor 0x{:04X}, device 0x{:04X}, "
|
||||
"driver version 0x{:X}",
|
||||
properties.deviceName, VK_VERSION_MAJOR(properties.apiVersion),
|
||||
VK_VERSION_MINOR(properties.apiVersion),
|
||||
VK_VERSION_PATCH(properties.apiVersion), properties.vendorID,
|
||||
properties.deviceID, properties.driverVersion);
|
||||
if (unclamped_api_version != properties.apiVersion) {
|
||||
XELOGI(
|
||||
"Device supports Vulkan API {}.{}.{}, but the used version is limited "
|
||||
"by the instance",
|
||||
VK_VERSION_MAJOR(unclamped_api_version),
|
||||
VK_VERSION_MINOR(unclamped_api_version),
|
||||
VK_VERSION_PATCH(unclamped_api_version));
|
||||
}
|
||||
|
||||
XELOGI("Enabled Vulkan device extensions:");
|
||||
for (uint32_t enabled_extension_index = 0;
|
||||
enabled_extension_index < device_create_info.enabledExtensionCount;
|
||||
++enabled_extension_index) {
|
||||
XELOGI("* {}",
|
||||
device_create_info.ppEnabledExtensionNames[enabled_extension_index]);
|
||||
}
|
||||
|
||||
XELOGI("Vulkan device properties and enabled features:");
|
||||
|
||||
VkPhysicalDeviceFeatures enabled_features = {};
|
||||
device_create_info.pEnabledFeatures = &enabled_features;
|
||||
|
||||
#define XE_UI_VULKAN_LIMIT(name) \
|
||||
device->properties_.name = properties.limits.name; \
|
||||
XELOGI("* " #name ": {}", properties.limits.name);
|
||||
#define XE_UI_VULKAN_ENUM_LIMIT(name, type) \
|
||||
device->properties_.name = properties.limits.name; \
|
||||
XELOGI("* " #name ": {}", vk::to_string(vk::type(properties.limits.name)));
|
||||
#define XE_UI_VULKAN_FEATURE(name) \
|
||||
enabled_features.name = supported_features.name; \
|
||||
device->properties_.name = supported_features.name; \
|
||||
if (supported_features.name) { \
|
||||
XELOGI("* " #name); \
|
||||
}
|
||||
#define XE_UI_VULKAN_PROPERTY_2(structure, name) \
|
||||
device->properties_.name = structure.name; \
|
||||
XELOGI("* " #name ": {}", structure.name);
|
||||
#define XE_UI_VULKAN_ENUM_PROPERTY_2(structure, name, type) \
|
||||
device->properties_.name = structure.name; \
|
||||
XELOGI("* " #name ": {}", vk::to_string(vk::type(structure.name)));
|
||||
#define XE_UI_VULKAN_FEATURE_2(structure, name) \
|
||||
structure.enabled.name = structure.supported.name; \
|
||||
device->properties_.name = structure.supported.name; \
|
||||
if (structure.supported.name) { \
|
||||
XELOGI("* " #name); \
|
||||
}
|
||||
#define XE_UI_VULKAN_FEATURE_IMPLIED(name) \
|
||||
device->properties_.name = true; \
|
||||
XELOGI("* " #name);
|
||||
|
||||
if (ext_1_2_KHR_driver_properties) {
|
||||
XE_UI_VULKAN_ENUM_PROPERTY_2(properties_1_2_KHR_driver_properties, driverID,
|
||||
DriverId);
|
||||
XELOGI("* driverName: {}", properties_1_2_KHR_driver_properties.driverName);
|
||||
if (properties_1_2_KHR_driver_properties.driverInfo[0]) {
|
||||
XELOGI("* driverInfo: {}",
|
||||
properties_1_2_KHR_driver_properties.driverInfo);
|
||||
}
|
||||
XELOGI("* conformanceVersion: {}.{}.{}.{}",
|
||||
properties_1_2_KHR_driver_properties.conformanceVersion.major,
|
||||
properties_1_2_KHR_driver_properties.conformanceVersion.minor,
|
||||
properties_1_2_KHR_driver_properties.conformanceVersion.subminor,
|
||||
properties_1_2_KHR_driver_properties.conformanceVersion.patch);
|
||||
}
|
||||
|
||||
XE_UI_VULKAN_LIMIT(maxImageDimension2D)
|
||||
XE_UI_VULKAN_LIMIT(maxImageDimension3D)
|
||||
XE_UI_VULKAN_LIMIT(maxImageDimensionCube)
|
||||
XE_UI_VULKAN_LIMIT(maxImageArrayLayers)
|
||||
XE_UI_VULKAN_LIMIT(maxStorageBufferRange)
|
||||
XE_UI_VULKAN_LIMIT(maxSamplerAllocationCount)
|
||||
XE_UI_VULKAN_LIMIT(maxPerStageDescriptorSamplers)
|
||||
XE_UI_VULKAN_LIMIT(maxPerStageDescriptorStorageBuffers)
|
||||
XE_UI_VULKAN_LIMIT(maxPerStageDescriptorSampledImages)
|
||||
XE_UI_VULKAN_LIMIT(maxPerStageResources)
|
||||
XE_UI_VULKAN_LIMIT(maxVertexOutputComponents)
|
||||
XE_UI_VULKAN_LIMIT(maxTessellationEvaluationOutputComponents)
|
||||
XE_UI_VULKAN_LIMIT(maxGeometryInputComponents)
|
||||
XE_UI_VULKAN_LIMIT(maxGeometryOutputComponents)
|
||||
XE_UI_VULKAN_LIMIT(maxFragmentInputComponents)
|
||||
XE_UI_VULKAN_LIMIT(maxFragmentCombinedOutputResources)
|
||||
XE_UI_VULKAN_LIMIT(maxSamplerAnisotropy)
|
||||
XE_UI_VULKAN_LIMIT(maxViewportDimensions[0])
|
||||
XE_UI_VULKAN_LIMIT(maxViewportDimensions[1])
|
||||
XE_UI_VULKAN_LIMIT(minUniformBufferOffsetAlignment)
|
||||
XE_UI_VULKAN_LIMIT(minStorageBufferOffsetAlignment)
|
||||
XE_UI_VULKAN_LIMIT(maxFramebufferWidth)
|
||||
XE_UI_VULKAN_LIMIT(maxFramebufferHeight)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(framebufferColorSampleCounts, SampleCountFlags)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(framebufferDepthSampleCounts, SampleCountFlags)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(framebufferStencilSampleCounts, SampleCountFlags)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(framebufferNoAttachmentsSampleCounts,
|
||||
SampleCountFlags)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(sampledImageColorSampleCounts, SampleCountFlags)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(sampledImageIntegerSampleCounts, SampleCountFlags)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(sampledImageDepthSampleCounts, SampleCountFlags)
|
||||
XE_UI_VULKAN_ENUM_LIMIT(sampledImageStencilSampleCounts, SampleCountFlags)
|
||||
XE_UI_VULKAN_LIMIT(standardSampleLocations)
|
||||
XE_UI_VULKAN_LIMIT(optimalBufferCopyOffsetAlignment)
|
||||
XE_UI_VULKAN_LIMIT(optimalBufferCopyRowPitchAlignment)
|
||||
XE_UI_VULKAN_LIMIT(nonCoherentAtomSize)
|
||||
|
||||
if (with_gpu_emulation) {
|
||||
XE_UI_VULKAN_FEATURE(robustBufferAccess)
|
||||
XE_UI_VULKAN_FEATURE(fullDrawIndexUint32)
|
||||
XE_UI_VULKAN_FEATURE(independentBlend)
|
||||
XE_UI_VULKAN_FEATURE(geometryShader)
|
||||
XE_UI_VULKAN_FEATURE(tessellationShader)
|
||||
XE_UI_VULKAN_FEATURE(sampleRateShading)
|
||||
XE_UI_VULKAN_FEATURE(depthClamp)
|
||||
XE_UI_VULKAN_FEATURE(fillModeNonSolid)
|
||||
XE_UI_VULKAN_FEATURE(samplerAnisotropy)
|
||||
XE_UI_VULKAN_FEATURE(occlusionQueryPrecise)
|
||||
XE_UI_VULKAN_FEATURE(vertexPipelineStoresAndAtomics)
|
||||
XE_UI_VULKAN_FEATURE(fragmentStoresAndAtomics)
|
||||
XE_UI_VULKAN_FEATURE(shaderClipDistance)
|
||||
XE_UI_VULKAN_FEATURE(shaderCullDistance)
|
||||
XE_UI_VULKAN_FEATURE(sparseBinding)
|
||||
XE_UI_VULKAN_FEATURE(sparseResidencyBuffer)
|
||||
}
|
||||
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 2, 0)) {
|
||||
if (with_gpu_emulation) {
|
||||
XE_UI_VULKAN_FEATURE_2(features_1_2, samplerMirrorClampToEdge);
|
||||
}
|
||||
} else {
|
||||
if (ext_1_2_KHR_sampler_mirror_clamp_to_edge) {
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(samplerMirrorClampToEdge)
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
|
||||
if (with_gpu_emulation) {
|
||||
XE_UI_VULKAN_FEATURE_2(features_1_3, shaderDemoteToHelperInvocation);
|
||||
}
|
||||
} else {
|
||||
if (ext_1_3_EXT_shader_demote_to_helper_invocation) {
|
||||
if (with_gpu_emulation) {
|
||||
XE_UI_VULKAN_FEATURE_2(
|
||||
features_1_3_EXT_shader_demote_to_helper_invocation,
|
||||
shaderDemoteToHelperInvocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_KHR_portability_subset) {
|
||||
if (with_gpu_emulation) {
|
||||
XE_UI_VULKAN_FEATURE_2(features_KHR_portability_subset,
|
||||
constantAlphaColorBlendFactors)
|
||||
XE_UI_VULKAN_FEATURE_2(features_KHR_portability_subset,
|
||||
imageViewFormatReinterpretation)
|
||||
XE_UI_VULKAN_FEATURE_2(features_KHR_portability_subset,
|
||||
imageViewFormatSwizzle)
|
||||
XE_UI_VULKAN_FEATURE_2(features_KHR_portability_subset, pointPolygons)
|
||||
XE_UI_VULKAN_FEATURE_2(features_KHR_portability_subset,
|
||||
separateStencilMaskRef)
|
||||
XE_UI_VULKAN_FEATURE_2(features_KHR_portability_subset,
|
||||
shaderSampleRateInterpolationFunctions)
|
||||
XE_UI_VULKAN_FEATURE_2(features_KHR_portability_subset, triangleFans)
|
||||
}
|
||||
} else {
|
||||
// Not a portability subset device.
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(constantAlphaColorBlendFactors)
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(imageViewFormatReinterpretation)
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(imageViewFormatSwizzle)
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(pointPolygons)
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(separateStencilMaskRef)
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(shaderSampleRateInterpolationFunctions)
|
||||
XE_UI_VULKAN_FEATURE_IMPLIED(triangleFans)
|
||||
}
|
||||
|
||||
if (ext_1_2_KHR_shader_float_controls) {
|
||||
XE_UI_VULKAN_PROPERTY_2(properties_1_2_KHR_shader_float_controls,
|
||||
shaderSignedZeroInfNanPreserveFloat32);
|
||||
XE_UI_VULKAN_PROPERTY_2(properties_1_2_KHR_shader_float_controls,
|
||||
shaderDenormFlushToZeroFloat32);
|
||||
XE_UI_VULKAN_PROPERTY_2(properties_1_2_KHR_shader_float_controls,
|
||||
shaderRoundingModeRTEFloat32);
|
||||
}
|
||||
|
||||
if (ext_EXT_fragment_shader_interlock) {
|
||||
if (with_gpu_emulation) {
|
||||
XE_UI_VULKAN_FEATURE_2(features_EXT_fragment_shader_interlock,
|
||||
fragmentShaderSampleInterlock)
|
||||
XE_UI_VULKAN_FEATURE_2(features_EXT_fragment_shader_interlock,
|
||||
fragmentShaderPixelInterlock)
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_EXT_non_seamless_cube_map) {
|
||||
if (with_gpu_emulation) {
|
||||
XE_UI_VULKAN_FEATURE_2(features_EXT_non_seamless_cube_map,
|
||||
nonSeamlessCubeMap)
|
||||
}
|
||||
}
|
||||
|
||||
#undef XE_UI_VULKAN_LIMIT
|
||||
#undef XE_UI_VULKAN_ENUM_LIMIT
|
||||
#undef XE_UI_VULKAN_FEATURE
|
||||
#undef XE_UI_VULKAN_PROPERTY_2
|
||||
#undef XE_UI_VULKAN_ENUM_PROPERTY_2
|
||||
#undef XE_UI_VULKAN_FEATURE_2
|
||||
|
||||
// Create the device.
|
||||
|
||||
const VkResult device_create_result = ifn.vkCreateDevice(
|
||||
physical_device, &device_create_info, nullptr, &device->device_);
|
||||
if (device_create_result != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"Failed to create a Vulkan logical device from the physical device "
|
||||
"'{}': {}",
|
||||
properties.deviceName, vk::to_string(vk::Result(device_create_result)));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Load device functions.
|
||||
|
||||
bool functions_loaded = true;
|
||||
|
||||
Functions& dfn = device->functions_;
|
||||
|
||||
#define XE_UI_VULKAN_FUNCTION(name) \
|
||||
functions_loaded &= (dfn.name = PFN_##name(ifn.vkGetDeviceProcAddr( \
|
||||
device->device_, #name))) != nullptr;
|
||||
|
||||
// Vulkan 1.0.
|
||||
#include "xenia/ui/vulkan/functions/device_1_0.inc"
|
||||
|
||||
// Extensions promoted to a Vulkan version supported by the device.
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
functions_loaded &= \
|
||||
(dfn.core_name = PFN_##core_name( \
|
||||
ifn.vkGetDeviceProcAddr(device->device_, #core_name))) != nullptr;
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
#include "xenia/ui/vulkan/functions/device_1_1_khr_bind_memory2.inc"
|
||||
#include "xenia/ui/vulkan/functions/device_1_1_khr_get_memory_requirements2.inc"
|
||||
}
|
||||
if (properties.apiVersion >= VK_MAKE_API_VERSION(0, 1, 3, 0)) {
|
||||
#include "xenia/ui/vulkan/functions/device_1_3_khr_maintenance4.inc"
|
||||
}
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
|
||||
// Non-promoted extensions, and extensions promoted to a Vulkan version not
|
||||
// supported by the device.
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
functions_loaded &= \
|
||||
(dfn.core_name = PFN_##core_name(ifn.vkGetDeviceProcAddr( \
|
||||
device->device_, #extension_name))) != nullptr;
|
||||
if (properties.apiVersion < VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
if (device->extensions_.ext_1_1_KHR_get_memory_requirements2) {
|
||||
#include "xenia/ui/vulkan/functions/device_1_1_khr_get_memory_requirements2.inc"
|
||||
}
|
||||
if (device->extensions_.ext_1_1_KHR_bind_memory2) {
|
||||
#include "xenia/ui/vulkan/functions/device_1_1_khr_bind_memory2.inc"
|
||||
}
|
||||
}
|
||||
if (properties.apiVersion < VK_MAKE_API_VERSION(0, 1, 3, 0)) {
|
||||
if (device->extensions_.ext_1_3_KHR_maintenance4) {
|
||||
#include "xenia/ui/vulkan/functions/device_1_3_khr_maintenance4.inc"
|
||||
}
|
||||
}
|
||||
if (device->extensions_.ext_KHR_swapchain) {
|
||||
#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc"
|
||||
}
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
|
||||
#undef XE_UI_VULKAN_FUNCTION
|
||||
|
||||
if (!functions_loaded) {
|
||||
XELOGE("Failed to get all Vulkan device function pointers for '{}'",
|
||||
properties.deviceName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the queues.
|
||||
|
||||
for (size_t queue_family_index = 0;
|
||||
queue_family_index < device->queue_families_.size();
|
||||
++queue_family_index) {
|
||||
QueueFamily& queue_family = device->queue_families_[queue_family_index];
|
||||
for (size_t queue_index = 0; queue_index < queue_family.queues.size();
|
||||
++queue_index) {
|
||||
VkQueue queue;
|
||||
dfn.vkGetDeviceQueue(device->device_, uint32_t(queue_family_index),
|
||||
uint32_t(queue_index), &queue);
|
||||
queue_family.queues[queue_index] = std::make_unique<Queue>(queue);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the memory types.
|
||||
|
||||
VkPhysicalDeviceMemoryProperties memory_properties;
|
||||
ifn.vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
|
||||
for (uint32_t memory_type_index = 0;
|
||||
memory_type_index < memory_properties.memoryTypeCount;
|
||||
++memory_type_index) {
|
||||
const uint32_t memory_type_bit = uint32_t(1) << memory_type_index;
|
||||
const VkMemoryPropertyFlags memory_type_flags =
|
||||
memory_properties.memoryTypes[memory_type_index].propertyFlags;
|
||||
if (memory_type_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
|
||||
device->memory_types_.device_local |= memory_type_bit;
|
||||
}
|
||||
if (memory_type_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
|
||||
device->memory_types_.host_visible |= memory_type_bit;
|
||||
}
|
||||
if (memory_type_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
|
||||
device->memory_types_.host_coherent |= memory_type_bit;
|
||||
}
|
||||
if (memory_type_flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
|
||||
device->memory_types_.host_cached |= memory_type_bit;
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
VulkanDevice::~VulkanDevice() {
|
||||
if (device_) {
|
||||
vulkan_instance_->functions().vkDestroyDevice(device_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanDevice::VulkanDevice(const VulkanInstance* const vulkan_instance,
|
||||
const VkPhysicalDevice physical_device)
|
||||
: vulkan_instance_(vulkan_instance), physical_device_(physical_device) {
|
||||
assert_not_null(vulkan_instance);
|
||||
assert_not_null(physical_device);
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
297
src/xenia/ui/vulkan/vulkan_device.h
Normal file
297
src/xenia/ui/vulkan/vulkan_device.h
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_VULKAN_VULKAN_DEVICE_H_
|
||||
#define XENIA_UI_VULKAN_VULKAN_DEVICE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/ui/vulkan/vulkan_instance.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
class VulkanDevice {
|
||||
public:
|
||||
static std::unique_ptr<VulkanDevice> CreateIfSupported(
|
||||
const VulkanInstance* vulkan_instance, VkPhysicalDevice physical_device,
|
||||
bool with_gpu_emulation, bool with_swapchain);
|
||||
|
||||
VulkanDevice(const VulkanDevice&) = delete;
|
||||
VulkanDevice& operator=(const VulkanDevice&) = delete;
|
||||
VulkanDevice(VulkanDevice&&) = delete;
|
||||
VulkanDevice& operator=(VulkanDevice&&) = delete;
|
||||
|
||||
~VulkanDevice();
|
||||
|
||||
const VulkanInstance* vulkan_instance() const { return vulkan_instance_; }
|
||||
|
||||
VkPhysicalDevice physical_device() const { return physical_device_; }
|
||||
|
||||
// If functionality from higher API versions is used, increase this.
|
||||
// This is for VkApplicationInfo.
|
||||
// "apiVersion must be the highest version of Vulkan that the application is
|
||||
// designed to use"
|
||||
// "The patch version number specified in apiVersion is ignored when creating
|
||||
// an instance object"
|
||||
static constexpr uint32_t kHighestUsedApiMinorVersion =
|
||||
VK_MAKE_API_VERSION(0, 1, 3, 0);
|
||||
|
||||
struct Properties {
|
||||
// Vulkan 1.0
|
||||
uint32_t apiVersion = VK_MAKE_API_VERSION(0, 1, 0, 0);
|
||||
uint32_t driverVersion = 0;
|
||||
uint32_t vendorID = 0;
|
||||
uint32_t deviceID = 0;
|
||||
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE] = {};
|
||||
|
||||
uint32_t maxImageDimension2D = 4096;
|
||||
uint32_t maxImageDimension3D = 256;
|
||||
uint32_t maxImageDimensionCube = 4096;
|
||||
uint32_t maxImageArrayLayers = 256;
|
||||
uint32_t maxStorageBufferRange = uint32_t(1) << 27;
|
||||
uint32_t maxSamplerAllocationCount = 4000;
|
||||
uint32_t maxPerStageDescriptorSamplers = 16;
|
||||
uint32_t maxPerStageDescriptorStorageBuffers = 4;
|
||||
uint32_t maxPerStageDescriptorSampledImages = 16;
|
||||
uint32_t maxPerStageResources = 128;
|
||||
uint32_t maxVertexOutputComponents = 64;
|
||||
uint32_t maxTessellationEvaluationOutputComponents = 64;
|
||||
uint32_t maxGeometryInputComponents = 64;
|
||||
uint32_t maxGeometryOutputComponents = 64;
|
||||
uint32_t maxFragmentInputComponents = 64;
|
||||
uint32_t maxFragmentCombinedOutputResources = 4;
|
||||
float maxSamplerAnisotropy = 1.0f;
|
||||
uint32_t maxViewportDimensions[2] = {4096, 4096};
|
||||
VkDeviceSize minUniformBufferOffsetAlignment = 256;
|
||||
VkDeviceSize minStorageBufferOffsetAlignment = 256;
|
||||
uint32_t maxFramebufferWidth = 4096;
|
||||
uint32_t maxFramebufferHeight = 4096;
|
||||
VkSampleCountFlags framebufferColorSampleCounts =
|
||||
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
|
||||
VkSampleCountFlags framebufferDepthSampleCounts =
|
||||
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
|
||||
VkSampleCountFlags framebufferStencilSampleCounts =
|
||||
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
|
||||
VkSampleCountFlags framebufferNoAttachmentsSampleCounts =
|
||||
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
|
||||
VkSampleCountFlags sampledImageColorSampleCounts =
|
||||
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
|
||||
VkSampleCountFlags sampledImageIntegerSampleCounts = VK_SAMPLE_COUNT_1_BIT;
|
||||
VkSampleCountFlags sampledImageDepthSampleCounts =
|
||||
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
|
||||
VkSampleCountFlags sampledImageStencilSampleCounts =
|
||||
VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT;
|
||||
bool standardSampleLocations = false;
|
||||
VkDeviceSize optimalBufferCopyOffsetAlignment = 1;
|
||||
VkDeviceSize optimalBufferCopyRowPitchAlignment = 1;
|
||||
VkDeviceSize nonCoherentAtomSize = 256;
|
||||
|
||||
bool robustBufferAccess = false;
|
||||
bool fullDrawIndexUint32 = false;
|
||||
bool independentBlend = false;
|
||||
bool geometryShader = false;
|
||||
bool tessellationShader = false;
|
||||
bool sampleRateShading = false;
|
||||
bool depthClamp = false;
|
||||
bool fillModeNonSolid = false;
|
||||
bool samplerAnisotropy = false;
|
||||
bool occlusionQueryPrecise = false;
|
||||
bool vertexPipelineStoresAndAtomics = false;
|
||||
bool fragmentStoresAndAtomics = false;
|
||||
bool shaderClipDistance = false;
|
||||
bool shaderCullDistance = false;
|
||||
bool sparseBinding = false;
|
||||
bool sparseResidencyBuffer = false;
|
||||
|
||||
// VK_KHR_sampler_mirror_clamp_to_edge (#15, promoted to 1.2)
|
||||
|
||||
bool samplerMirrorClampToEdge = false;
|
||||
|
||||
// VK_KHR_portability_subset (#164)
|
||||
|
||||
bool constantAlphaColorBlendFactors = false;
|
||||
bool imageViewFormatReinterpretation = false;
|
||||
bool imageViewFormatSwizzle = false;
|
||||
bool pointPolygons = false;
|
||||
bool separateStencilMaskRef = false;
|
||||
bool shaderSampleRateInterpolationFunctions = false;
|
||||
bool triangleFans = false;
|
||||
|
||||
// VK_KHR_driver_properties (#197, promoted to 1.2)
|
||||
|
||||
VkDriverId driverID = VkDriverId(0);
|
||||
|
||||
// VK_KHR_shader_float_controls (#198, promoted to 1.2)
|
||||
|
||||
bool shaderSignedZeroInfNanPreserveFloat32 = false;
|
||||
bool shaderDenormFlushToZeroFloat32 = false;
|
||||
bool shaderRoundingModeRTEFloat32 = false;
|
||||
|
||||
// VK_EXT_fragment_shader_interlock (#252)
|
||||
|
||||
bool fragmentShaderSampleInterlock = false;
|
||||
bool fragmentShaderPixelInterlock = false;
|
||||
|
||||
// VK_EXT_shader_demote_to_helper_invocation (#277, promoted to 1.3)
|
||||
|
||||
bool shaderDemoteToHelperInvocation = false;
|
||||
|
||||
// VK_EXT_non_seamless_cube_map (#423)
|
||||
|
||||
bool nonSeamlessCubeMap = false;
|
||||
};
|
||||
|
||||
// Properties of the core API and enabled extensions, and enabled features.
|
||||
// Some supported functionality is enabled conditionally based on the
|
||||
// `with_swapchain` and `with_gpu_emulation` options.
|
||||
const Properties& properties() const { return properties_; }
|
||||
|
||||
// Enabled extensions not fully covered by the device properties and optional
|
||||
// feature flags in the `Properties` structure (primarily those adding API
|
||||
// functionality rather than GPU features). Also set to true if the version of
|
||||
// the Vulkan API they were promoted to it supported (with the
|
||||
// `ext_major_minor_` prefix rather than `ext_`).
|
||||
struct Extensions {
|
||||
bool ext_KHR_swapchain = false; // #2
|
||||
bool ext_1_1_KHR_dedicated_allocation = false; // #128
|
||||
bool ext_EXT_shader_stencil_export = false; // #141
|
||||
bool ext_1_1_KHR_get_memory_requirements2 = false; // #147
|
||||
bool ext_1_2_KHR_image_format_list = false; // #148
|
||||
// Has optional features not implied by this being true.
|
||||
bool ext_1_1_KHR_sampler_ycbcr_conversion = false; // #157
|
||||
bool ext_1_1_KHR_bind_memory2 = false; // #158
|
||||
bool ext_1_2_KHR_spirv_1_4 = false; // #237
|
||||
bool ext_EXT_memory_budget = false; // #238
|
||||
// Has optional features not implied by this being true.
|
||||
bool ext_1_3_KHR_maintenance4 = false; // #414
|
||||
};
|
||||
|
||||
const Extensions& extensions() const { return extensions_; }
|
||||
|
||||
VkDevice device() const { return device_; }
|
||||
|
||||
struct Functions {
|
||||
#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name = nullptr;
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
PFN_##core_name core_name = nullptr;
|
||||
#include "xenia/ui/vulkan/functions/device_1_0.inc"
|
||||
// VK_KHR_swapchain (#2)
|
||||
#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc"
|
||||
// VK_KHR_get_memory_requirements2 (#147, promoted to 1.1)
|
||||
#include "xenia/ui/vulkan/functions/device_1_1_khr_get_memory_requirements2.inc"
|
||||
// VK_KHR_bind_memory2 (#158, promoted to 1.1)
|
||||
#include "xenia/ui/vulkan/functions/device_1_1_khr_bind_memory2.inc"
|
||||
// VK_KHR_maintenance4 (#414, promoted to 1.3)
|
||||
#include "xenia/ui/vulkan/functions/device_1_3_khr_maintenance4.inc"
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
#undef XE_UI_VULKAN_FUNCTION
|
||||
};
|
||||
|
||||
const Functions& functions() const { return functions_; }
|
||||
|
||||
template <typename Object>
|
||||
void SetObjectName(const VkObjectType object_type, const Object object_handle,
|
||||
const char* const object_name) const {
|
||||
if (!vulkan_instance()->extensions().ext_EXT_debug_utils) {
|
||||
return;
|
||||
}
|
||||
VkDebugUtilsObjectNameInfoEXT object_name_info;
|
||||
object_name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
object_name_info.pNext = nullptr;
|
||||
object_name_info.objectType = object_type;
|
||||
object_name_info.objectHandle = (uint64_t)object_handle;
|
||||
object_name_info.pObjectName = object_name;
|
||||
vulkan_instance()->functions().vkSetDebugUtilsObjectNameEXT(
|
||||
device(), &object_name_info);
|
||||
}
|
||||
|
||||
struct Queue {
|
||||
// Host access to queues must be externally synchronized in Vulkan.
|
||||
std::recursive_mutex mutex;
|
||||
VkQueue queue = nullptr;
|
||||
|
||||
explicit Queue(const VkQueue queue) : queue(queue) {}
|
||||
|
||||
class Acquisition {
|
||||
public:
|
||||
explicit Acquisition(Queue& queue)
|
||||
: lock_(queue.mutex), queue_(queue.queue) {}
|
||||
|
||||
VkQueue queue() const { return queue_; }
|
||||
|
||||
private:
|
||||
std::unique_lock<std::recursive_mutex> lock_;
|
||||
VkQueue queue_;
|
||||
};
|
||||
|
||||
Acquisition Acquire() { return Acquisition(*this); }
|
||||
};
|
||||
|
||||
struct QueueFamily {
|
||||
VkQueueFlags queue_flags = 0;
|
||||
bool may_support_presentation = false;
|
||||
std::vector<std::unique_ptr<Queue>> queues;
|
||||
};
|
||||
|
||||
const std::vector<QueueFamily>& queue_families() const {
|
||||
return queue_families_;
|
||||
}
|
||||
uint32_t queue_family_graphics_compute() const {
|
||||
return queue_family_graphics_compute_;
|
||||
}
|
||||
// UINT32_MAX if not supported or not enabled.
|
||||
// May be the same as queue_family_graphics_compute().
|
||||
uint32_t queue_family_sparse_binding() const {
|
||||
return queue_family_sparse_binding_;
|
||||
}
|
||||
|
||||
Queue::Acquisition AcquireQueue(const uint32_t queue_family_index,
|
||||
const uint32_t queue_index) const {
|
||||
return queue_families()[queue_family_index].queues[queue_index]->Acquire();
|
||||
}
|
||||
|
||||
struct MemoryTypes {
|
||||
uint32_t device_local = 0b0;
|
||||
uint32_t host_visible = 0b0;
|
||||
uint32_t host_coherent = 0b0;
|
||||
uint32_t host_cached = 0b0;
|
||||
};
|
||||
|
||||
const MemoryTypes& memory_types() const { return memory_types_; }
|
||||
|
||||
private:
|
||||
explicit VulkanDevice(const VulkanInstance* vulkan_instance,
|
||||
VkPhysicalDevice physical_device);
|
||||
|
||||
const VulkanInstance* vulkan_instance_ = nullptr;
|
||||
VkPhysicalDevice physical_device_ = nullptr;
|
||||
|
||||
Properties properties_;
|
||||
Extensions extensions_;
|
||||
|
||||
VkDevice device_ = nullptr;
|
||||
|
||||
Functions functions_;
|
||||
|
||||
std::vector<QueueFamily> queue_families_;
|
||||
uint32_t queue_family_graphics_compute_ = UINT32_MAX;
|
||||
uint32_t queue_family_sparse_binding_ = UINT32_MAX;
|
||||
|
||||
MemoryTypes memory_types_;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_VULKAN_VULKAN_DEVICE_H_
|
||||
|
|
@ -30,6 +30,20 @@ namespace shaders {
|
|||
#include "xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vs.h"
|
||||
} // namespace shaders
|
||||
|
||||
std::unique_ptr<VulkanImmediateDrawer> VulkanImmediateDrawer::Create(
|
||||
const VulkanDevice* const vulkan_device,
|
||||
const UISamplers* const ui_samplers) {
|
||||
assert_not_null(vulkan_device);
|
||||
assert_not_null(ui_samplers);
|
||||
|
||||
auto immediate_drawer = std::unique_ptr<VulkanImmediateDrawer>(
|
||||
new VulkanImmediateDrawer(vulkan_device, ui_samplers));
|
||||
if (!immediate_drawer->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return immediate_drawer;
|
||||
}
|
||||
|
||||
VulkanImmediateDrawer::VulkanImmediateTexture::~VulkanImmediateTexture() {
|
||||
if (immediate_drawer_) {
|
||||
immediate_drawer_->OnImmediateTextureDestroyed(*this);
|
||||
|
|
@ -45,8 +59,8 @@ VulkanImmediateDrawer::~VulkanImmediateDrawer() {
|
|||
last_paint_submission_index_);
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_line_);
|
||||
util::DestroyAndNullHandle(dfn.vkDestroyPipeline, device, pipeline_triangle_);
|
||||
|
|
@ -88,9 +102,17 @@ VulkanImmediateDrawer::~VulkanImmediateDrawer() {
|
|||
texture_descriptor_set_layout_);
|
||||
}
|
||||
|
||||
VulkanImmediateDrawer::VulkanImmediateDrawer(
|
||||
const VulkanDevice* const vulkan_device,
|
||||
const UISamplers* const ui_samplers)
|
||||
: vulkan_device_(vulkan_device), ui_samplers_(ui_samplers) {
|
||||
assert_not_null(vulkan_device);
|
||||
assert_not_null(ui_samplers);
|
||||
}
|
||||
|
||||
bool VulkanImmediateDrawer::Initialize() {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkDescriptorSetLayoutBinding texture_descriptor_set_layout_binding;
|
||||
texture_descriptor_set_layout_binding.binding = 0;
|
||||
|
|
@ -128,7 +150,7 @@ bool VulkanImmediateDrawer::Initialize() {
|
|||
}
|
||||
|
||||
vertex_buffer_pool_ = std::make_unique<VulkanUploadBufferPool>(
|
||||
provider_,
|
||||
vulkan_device_,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
|
||||
|
||||
VkPushConstantRange push_constant_ranges[1];
|
||||
|
|
@ -194,8 +216,8 @@ void VulkanImmediateDrawer::Begin(UIDrawContext& ui_draw_context,
|
|||
last_completed_submission_index_ =
|
||||
vulkan_ui_draw_context.submission_index_completed();
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
// Destroy deleted textures.
|
||||
for (auto it = textures_deleted_.begin(); it != textures_deleted_.end();) {
|
||||
|
|
@ -272,7 +294,7 @@ void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
|
|||
VkCommandBuffer draw_command_buffer =
|
||||
vulkan_ui_draw_context.draw_command_buffer();
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
|
||||
// Bind the vertices.
|
||||
size_t vertex_buffer_size = sizeof(ImmediateVertex) * batch.vertex_count;
|
||||
|
|
@ -319,7 +341,7 @@ void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
|||
return;
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VulkanUIDrawContext& vulkan_ui_draw_context =
|
||||
*static_cast<const VulkanUIDrawContext*>(ui_draw_context());
|
||||
VkCommandBuffer draw_command_buffer =
|
||||
|
|
@ -410,7 +432,7 @@ void VulkanImmediateDrawer::End() {
|
|||
vulkan_presenter.AcquireUISetupCommandBufferFromUIThread();
|
||||
if (setup_command_buffer != VK_NULL_HANDLE) {
|
||||
size_t texture_uploads_pending_count = texture_uploads_pending_.size();
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VulkanUIDrawContext& vulkan_ui_draw_context =
|
||||
*static_cast<const VulkanUIDrawContext*>(ui_draw_context());
|
||||
|
||||
|
|
@ -528,8 +550,8 @@ void VulkanImmediateDrawer::OnLeavePresenter() {
|
|||
texture->last_usage_submission_ = 0;
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
for (SubmittedTextureUploadBuffer& submitted_texture_upload_buffer :
|
||||
texture_upload_buffers_submitted_) {
|
||||
|
|
@ -566,8 +588,8 @@ bool VulkanImmediateDrawer::EnsurePipelinesCreatedForCurrentRenderPass() {
|
|||
last_paint_submission_index_);
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
// Safe to destroy the pipelines now - if the render pass was recreated,
|
||||
// completion of its usage has already been awaited.
|
||||
|
|
@ -581,8 +603,8 @@ bool VulkanImmediateDrawer::EnsurePipelinesCreatedForCurrentRenderPass() {
|
|||
VkPipelineShaderStageCreateInfo stages[2] = {};
|
||||
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
stages[0].module = util::CreateShaderModule(provider_, shaders::immediate_vs,
|
||||
sizeof(shaders::immediate_vs));
|
||||
stages[0].module = util::CreateShaderModule(
|
||||
vulkan_device_, shaders::immediate_vs, sizeof(shaders::immediate_vs));
|
||||
if (stages[0].module == VK_NULL_HANDLE) {
|
||||
XELOGE("VulkanImmediateDrawer: Failed to create the vertex shader module");
|
||||
return false;
|
||||
|
|
@ -590,8 +612,8 @@ bool VulkanImmediateDrawer::EnsurePipelinesCreatedForCurrentRenderPass() {
|
|||
stages[0].pName = "main";
|
||||
stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
stages[1].module = util::CreateShaderModule(provider_, shaders::immediate_ps,
|
||||
sizeof(shaders::immediate_ps));
|
||||
stages[1].module = util::CreateShaderModule(
|
||||
vulkan_device_, shaders::immediate_ps, sizeof(shaders::immediate_ps));
|
||||
if (stages[1].module == VK_NULL_HANDLE) {
|
||||
XELOGE(
|
||||
"VulkanImmediateDrawer: Failed to create the fragment shader module");
|
||||
|
|
@ -753,8 +775,8 @@ uint32_t VulkanImmediateDrawer::AllocateTextureDescriptor() {
|
|||
return (pool->index << 6) | local_index;
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkDescriptorSetAllocateInfo allocate_info;
|
||||
allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
|
|
@ -864,8 +886,8 @@ bool VulkanImmediateDrawer::CreateTextureResource(
|
|||
bool is_repeated, const uint8_t* data,
|
||||
VulkanImmediateTexture::Resource& resource_out,
|
||||
size_t& pending_upload_index_out) {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
// Create the image and the descriptor.
|
||||
|
||||
|
|
@ -890,7 +912,7 @@ bool VulkanImmediateDrawer::CreateTextureResource(
|
|||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkImage image;
|
||||
VkDeviceMemory image_memory;
|
||||
if (!util::CreateDedicatedAllocationImage(provider_, image_create_info,
|
||||
if (!util::CreateDedicatedAllocationImage(vulkan_device_, image_create_info,
|
||||
util::MemoryPurpose::kDeviceLocal,
|
||||
image, image_memory)) {
|
||||
XELOGE(
|
||||
|
|
@ -910,7 +932,7 @@ bool VulkanImmediateDrawer::CreateTextureResource(
|
|||
// data == nullptr is a special case for (1, 1, 1, 1), though the image will
|
||||
// be cleared to (1, 1, 1, 1) anyway, just a micro-optimization.
|
||||
VkComponentSwizzle swizzle =
|
||||
(data || !provider_.device_info().imageViewFormatSwizzle)
|
||||
(data || !vulkan_device_->properties().imageViewFormatSwizzle)
|
||||
? VK_COMPONENT_SWIZZLE_IDENTITY
|
||||
: VK_COMPONENT_SWIZZLE_ONE;
|
||||
image_view_create_info.components.r = swizzle;
|
||||
|
|
@ -940,15 +962,16 @@ bool VulkanImmediateDrawer::CreateTextureResource(
|
|||
return false;
|
||||
}
|
||||
VkDescriptorImageInfo descriptor_image_info;
|
||||
VulkanProvider::HostSampler host_sampler;
|
||||
UISamplers::SamplerIndex ui_sampler_index;
|
||||
if (filter == ImmediateTextureFilter::kLinear) {
|
||||
host_sampler = is_repeated ? VulkanProvider::HostSampler::kLinearRepeat
|
||||
: VulkanProvider::HostSampler::kLinearClamp;
|
||||
ui_sampler_index = is_repeated ? UISamplers::kSamplerIndexLinearRepeat
|
||||
: UISamplers::kSamplerIndexLinearClampToEdge;
|
||||
} else {
|
||||
host_sampler = is_repeated ? VulkanProvider::HostSampler::kNearestRepeat
|
||||
: VulkanProvider::HostSampler::kNearestClamp;
|
||||
ui_sampler_index = is_repeated
|
||||
? UISamplers::kSamplerIndexNearestRepeat
|
||||
: UISamplers::kSamplerIndexNearestClampToEdge;
|
||||
}
|
||||
descriptor_image_info.sampler = provider_.GetHostSampler(host_sampler);
|
||||
descriptor_image_info.sampler = ui_samplers_->samplers()[ui_sampler_index];
|
||||
descriptor_image_info.imageView = image_view;
|
||||
descriptor_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
VkWriteDescriptorSet descriptor_write;
|
||||
|
|
@ -974,7 +997,7 @@ bool VulkanImmediateDrawer::CreateTextureResource(
|
|||
size_t data_size = sizeof(uint32_t) * width * height;
|
||||
uint32_t upload_buffer_memory_type;
|
||||
if (!util::CreateDedicatedAllocationBuffer(
|
||||
provider_, VkDeviceSize(data_size),
|
||||
vulkan_device_, VkDeviceSize(data_size),
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, util::MemoryPurpose::kUpload,
|
||||
upload_buffer, upload_buffer_memory, &upload_buffer_memory_type)) {
|
||||
XELOGE(
|
||||
|
|
@ -1003,7 +1026,7 @@ bool VulkanImmediateDrawer::CreateTextureResource(
|
|||
return false;
|
||||
}
|
||||
std::memcpy(upload_buffer_mapping, data, data_size);
|
||||
util::FlushMappedMemoryRange(provider_, upload_buffer_memory,
|
||||
util::FlushMappedMemoryRange(vulkan_device_, upload_buffer_memory,
|
||||
upload_buffer_memory_type);
|
||||
dfn.vkUnmapMemory(device, upload_buffer_memory);
|
||||
}
|
||||
|
|
@ -1030,8 +1053,8 @@ bool VulkanImmediateDrawer::CreateTextureResource(
|
|||
void VulkanImmediateDrawer::DestroyTextureResource(
|
||||
VulkanImmediateTexture::Resource& resource) {
|
||||
FreeTextureDescriptor(resource.descriptor_index);
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
dfn.vkDestroyImageView(device, resource.image_view, nullptr);
|
||||
dfn.vkDestroyImage(device, resource.image, nullptr);
|
||||
dfn.vkFreeMemory(device, resource.memory, nullptr);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "xenia/ui/immediate_drawer.h"
|
||||
#include "xenia/ui/vulkan/ui_samplers.h"
|
||||
#include "xenia/ui/vulkan/vulkan_upload_buffer_pool.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
@ -26,14 +27,7 @@ namespace vulkan {
|
|||
class VulkanImmediateDrawer : public ImmediateDrawer {
|
||||
public:
|
||||
static std::unique_ptr<VulkanImmediateDrawer> Create(
|
||||
const VulkanProvider& provider) {
|
||||
auto immediate_drawer = std::unique_ptr<VulkanImmediateDrawer>(
|
||||
new VulkanImmediateDrawer(provider));
|
||||
if (!immediate_drawer->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::move(immediate_drawer);
|
||||
}
|
||||
const VulkanDevice* vulkan_device, const UISamplers* ui_samplers);
|
||||
|
||||
~VulkanImmediateDrawer();
|
||||
|
||||
|
|
@ -96,7 +90,8 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
|
|||
TextureDescriptorPool* recycled_next;
|
||||
};
|
||||
|
||||
VulkanImmediateDrawer(const VulkanProvider& provider) : provider_(provider) {}
|
||||
explicit VulkanImmediateDrawer(const VulkanDevice* vulkan_device,
|
||||
const UISamplers* ui_samplers);
|
||||
bool Initialize();
|
||||
|
||||
bool EnsurePipelinesCreatedForCurrentRenderPass();
|
||||
|
|
@ -117,7 +112,8 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
|
|||
void DestroyTextureResource(VulkanImmediateTexture::Resource& resource);
|
||||
void OnImmediateTextureDestroyed(VulkanImmediateTexture& texture);
|
||||
|
||||
const VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
const UISamplers* ui_samplers_;
|
||||
|
||||
// Combined image sampler pools for textures.
|
||||
VkDescriptorSetLayout texture_descriptor_set_layout_;
|
||||
|
|
|
|||
667
src/xenia/ui/vulkan/vulkan_instance.cc
Normal file
667
src/xenia/ui/vulkan/vulkan_instance.cc
Normal file
|
|
@ -0,0 +1,667 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/vulkan/vulkan_instance.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/ui/vulkan/vulkan_presenter.h"
|
||||
|
||||
#if XE_PLATFORM_LINUX
|
||||
#include <dlfcn.h>
|
||||
#elif XE_PLATFORM_WIN32
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif
|
||||
|
||||
DEFINE_bool(
|
||||
vulkan_log_debug_messages, true,
|
||||
"Write Vulkan VK_EXT_debug_utils messages to the Xenia log, as opposed to "
|
||||
"the OS debug output.",
|
||||
"Vulkan");
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
std::unique_ptr<VulkanInstance> VulkanInstance::Create(
|
||||
const bool with_surface, const bool try_enable_validation) {
|
||||
std::unique_ptr<VulkanInstance> vulkan_instance(new VulkanInstance());
|
||||
|
||||
// Load the RenderDoc API if connected.
|
||||
|
||||
vulkan_instance->renderdoc_api_ = RenderDocAPI::CreateIfConnected();
|
||||
|
||||
// Load the loader library.
|
||||
|
||||
Functions& ifn = vulkan_instance->functions_;
|
||||
|
||||
bool functions_loaded = true;
|
||||
#if XE_PLATFORM_LINUX
|
||||
#if XE_PLATFORM_ANDROID
|
||||
const char* const loader_library_name = "libvulkan.so";
|
||||
#else
|
||||
const char* const loader_library_name = "libvulkan.so.1";
|
||||
#endif
|
||||
// http://developer.download.nvidia.com/mobile/shield/assets/Vulkan/UsingtheVulkanAPI.pdf
|
||||
vulkan_instance->loader_ = dlopen(loader_library_name, RTLD_NOW | RTLD_LOCAL);
|
||||
if (!vulkan_instance->loader_) {
|
||||
XELOGE("Failed to load {}", loader_library_name);
|
||||
return false;
|
||||
}
|
||||
#define XE_VULKAN_LOAD_LOADER_FUNCTION(name) \
|
||||
functions_loaded &= \
|
||||
(ifn.name = PFN_##name(dlsym(vulkan_instance->loader_, #name))) != \
|
||||
nullptr;
|
||||
#elif XE_PLATFORM_WIN32
|
||||
vulkan_instance->loader_ = LoadLibraryW(L"vulkan-1.dll");
|
||||
if (!vulkan_instance->loader_) {
|
||||
XELOGE("Failed to load vulkan-1.dll");
|
||||
return false;
|
||||
}
|
||||
#define XE_VULKAN_LOAD_LOADER_FUNCTION(name) \
|
||||
functions_loaded &= (ifn.name = PFN_##name(GetProcAddress( \
|
||||
vulkan_instance->loader_, #name))) != nullptr;
|
||||
#else
|
||||
#error No Vulkan loader library loading provided for the target platform.
|
||||
#endif
|
||||
XE_VULKAN_LOAD_LOADER_FUNCTION(vkGetInstanceProcAddr);
|
||||
XE_VULKAN_LOAD_LOADER_FUNCTION(vkDestroyInstance);
|
||||
#undef XE_VULKAN_LOAD_LOADER_FUNCTION
|
||||
if (!functions_loaded) {
|
||||
XELOGE("Failed to get Vulkan loader function pointers");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Load global functions.
|
||||
|
||||
functions_loaded &=
|
||||
(ifn.vkCreateInstance = PFN_vkCreateInstance(
|
||||
ifn.vkGetInstanceProcAddr(nullptr, "vkCreateInstance"))) != nullptr;
|
||||
functions_loaded &=
|
||||
(ifn.vkEnumerateInstanceExtensionProperties =
|
||||
PFN_vkEnumerateInstanceExtensionProperties(ifn.vkGetInstanceProcAddr(
|
||||
nullptr, "vkEnumerateInstanceExtensionProperties"))) != nullptr;
|
||||
functions_loaded &=
|
||||
(ifn.vkEnumerateInstanceLayerProperties =
|
||||
PFN_vkEnumerateInstanceLayerProperties(ifn.vkGetInstanceProcAddr(
|
||||
nullptr, "vkEnumerateInstanceLayerProperties"))) != nullptr;
|
||||
if (!functions_loaded) {
|
||||
XELOGE(
|
||||
"Failed to get Vulkan global function pointers via "
|
||||
"vkGetInstanceProcAddr");
|
||||
return nullptr;
|
||||
}
|
||||
// Available since Vulkan 1.1. If this is nullptr, it's a Vulkan 1.0 instance.
|
||||
ifn.vkEnumerateInstanceVersion = PFN_vkEnumerateInstanceVersion(
|
||||
ifn.vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"));
|
||||
|
||||
// Get the API version.
|
||||
|
||||
if (ifn.vkEnumerateInstanceVersion) {
|
||||
ifn.vkEnumerateInstanceVersion(&vulkan_instance->api_version_);
|
||||
}
|
||||
|
||||
// Enable extensions and layers.
|
||||
|
||||
// Name pointers from `requested_extensions` will be used in the enabled
|
||||
// extensions vector.
|
||||
std::unordered_map<std::string, bool*> requested_extensions;
|
||||
if (vulkan_instance->api_version_ >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
vulkan_instance->extensions_.ext_1_1_KHR_get_physical_device_properties2 =
|
||||
true;
|
||||
} else {
|
||||
// #60.
|
||||
requested_extensions.emplace(
|
||||
"VK_KHR_get_physical_device_properties2",
|
||||
&vulkan_instance->extensions_
|
||||
.ext_1_1_KHR_get_physical_device_properties2);
|
||||
}
|
||||
// #129.
|
||||
requested_extensions.emplace(
|
||||
"VK_EXT_debug_utils", &vulkan_instance->extensions_.ext_EXT_debug_utils);
|
||||
// #395.
|
||||
requested_extensions.emplace(
|
||||
"VK_KHR_portability_enumeration",
|
||||
&vulkan_instance->extensions_.ext_KHR_portability_enumeration);
|
||||
if (with_surface) {
|
||||
// #1.
|
||||
requested_extensions.emplace("VK_KHR_surface",
|
||||
&vulkan_instance->extensions_.ext_KHR_surface);
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
// #6.
|
||||
requested_extensions.emplace(
|
||||
"VK_KHR_xcb_surface",
|
||||
&vulkan_instance->extensions_.ext_KHR_xcb_surface);
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||
// #9.
|
||||
requested_extensions.emplace(
|
||||
"VK_KHR_android_surface",
|
||||
&vulkan_instance->extensions_.ext_KHR_android_surface);
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
// #10.
|
||||
requested_extensions.emplace(
|
||||
"VK_KHR_win32_surface",
|
||||
&vulkan_instance->extensions_.ext_KHR_win32_surface);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<const char*> enabled_extensions;
|
||||
|
||||
std::vector<VkExtensionProperties> supported_implementation_extensions;
|
||||
while (true) {
|
||||
uint32_t supported_implementation_extension_count = 0;
|
||||
const VkResult get_supported_implementation_extension_count_result =
|
||||
ifn.vkEnumerateInstanceExtensionProperties(
|
||||
nullptr, &supported_implementation_extension_count, nullptr);
|
||||
if (get_supported_implementation_extension_count_result != VK_SUCCESS &&
|
||||
get_supported_implementation_extension_count_result != VK_INCOMPLETE) {
|
||||
XELOGW("Failed to get the Vulkan instance extension count");
|
||||
return nullptr;
|
||||
}
|
||||
if (supported_implementation_extension_count) {
|
||||
supported_implementation_extensions.resize(
|
||||
supported_implementation_extension_count);
|
||||
const VkResult get_supported_implementation_extensions_result =
|
||||
ifn.vkEnumerateInstanceExtensionProperties(
|
||||
nullptr, &supported_implementation_extension_count,
|
||||
supported_implementation_extensions.data());
|
||||
if (get_supported_implementation_extensions_result == VK_INCOMPLETE) {
|
||||
continue;
|
||||
}
|
||||
if (get_supported_implementation_extensions_result != VK_SUCCESS) {
|
||||
XELOGW("Failed to get the Vulkan instance extensions");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
supported_implementation_extensions.resize(
|
||||
supported_implementation_extension_count);
|
||||
break;
|
||||
}
|
||||
|
||||
for (const VkExtensionProperties& supported_extension :
|
||||
supported_implementation_extensions) {
|
||||
const auto requested_extension_it =
|
||||
requested_extensions.find(supported_extension.extensionName);
|
||||
if (requested_extension_it == requested_extensions.cend()) {
|
||||
continue;
|
||||
}
|
||||
assert_not_null(requested_extension_it->second);
|
||||
if (!*requested_extension_it->second) {
|
||||
enabled_extensions.emplace_back(requested_extension_it->first.c_str());
|
||||
*requested_extension_it->second = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If enabled layers are not present, will disable all extensions provided by
|
||||
// the layers by truncating the enabled extension vector to this size.
|
||||
const size_t enabled_implementation_extension_count =
|
||||
enabled_extensions.size();
|
||||
std::vector<bool*> enabled_layer_extension_enablement_bools;
|
||||
|
||||
// Name pointers from `requested_layers` will be used in the enabled layer
|
||||
// vector.
|
||||
std::unordered_map<std::string, bool*> requested_layers;
|
||||
bool layer_khronos_validation = false;
|
||||
if (try_enable_validation) {
|
||||
requested_layers.emplace("VK_LAYER_KHRONOS_validation",
|
||||
&layer_khronos_validation);
|
||||
}
|
||||
|
||||
std::vector<const char*> enabled_layers;
|
||||
|
||||
if (!requested_layers.empty()) {
|
||||
std::vector<VkLayerProperties> available_layers;
|
||||
// "The list of available layers may change at any time due to actions
|
||||
// outside of the Vulkan implementation"
|
||||
while (true) {
|
||||
available_layers.clear();
|
||||
uint32_t available_layer_count = 0;
|
||||
const VkResult get_available_layer_count_result =
|
||||
ifn.vkEnumerateInstanceLayerProperties(&available_layer_count,
|
||||
nullptr);
|
||||
if (get_available_layer_count_result != VK_SUCCESS &&
|
||||
get_available_layer_count_result != VK_INCOMPLETE) {
|
||||
break;
|
||||
}
|
||||
if (available_layer_count) {
|
||||
available_layers.resize(available_layer_count);
|
||||
const VkResult get_available_layers_result =
|
||||
ifn.vkEnumerateInstanceLayerProperties(&available_layer_count,
|
||||
available_layers.data());
|
||||
if (get_available_layers_result == VK_INCOMPLETE) {
|
||||
// New layers were added.
|
||||
continue;
|
||||
}
|
||||
if (get_available_layers_result != VK_SUCCESS) {
|
||||
available_layers.clear();
|
||||
break;
|
||||
}
|
||||
// In case the second enumeration returned fewer layers.
|
||||
available_layers.resize(available_layer_count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!available_layers.empty()) {
|
||||
std::vector<VkExtensionProperties> supported_layer_extensions;
|
||||
|
||||
for (const VkLayerProperties& available_layer : available_layers) {
|
||||
auto requested_layer_it =
|
||||
requested_layers.find(available_layer.layerName);
|
||||
if (requested_layer_it == requested_layers.cend()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool got_layer_extensions = true;
|
||||
// "Because the list of available layers may change externally between
|
||||
// calls to vkEnumerateInstanceExtensionProperties, two calls may
|
||||
// retrieve different results if a pLayerName is available in one call
|
||||
// but not in another."
|
||||
while (true) {
|
||||
uint32_t supported_layer_extension_count = 0;
|
||||
const VkResult get_supported_layer_extension_count_result =
|
||||
ifn.vkEnumerateInstanceExtensionProperties(
|
||||
nullptr, &supported_layer_extension_count, nullptr);
|
||||
if (get_supported_layer_extension_count_result != VK_SUCCESS &&
|
||||
get_supported_layer_extension_count_result != VK_INCOMPLETE) {
|
||||
got_layer_extensions = false;
|
||||
break;
|
||||
}
|
||||
if (supported_layer_extension_count) {
|
||||
supported_layer_extensions.resize(supported_layer_extension_count);
|
||||
const VkResult get_supported_layer_extensions_result =
|
||||
ifn.vkEnumerateInstanceExtensionProperties(
|
||||
available_layer.layerName, &supported_layer_extension_count,
|
||||
supported_layer_extensions.data());
|
||||
if (get_supported_layer_extensions_result == VK_INCOMPLETE) {
|
||||
continue;
|
||||
}
|
||||
if (get_supported_layer_extensions_result != VK_SUCCESS) {
|
||||
got_layer_extensions = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
supported_layer_extensions.resize(supported_layer_extension_count);
|
||||
break;
|
||||
}
|
||||
if (!got_layer_extensions) {
|
||||
// The layer was possibly removed.
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const VkExtensionProperties& supported_extension :
|
||||
supported_layer_extensions) {
|
||||
const auto requested_extension_it =
|
||||
requested_extensions.find(supported_extension.extensionName);
|
||||
if (requested_extension_it == requested_extensions.cend()) {
|
||||
continue;
|
||||
}
|
||||
assert_not_null(requested_extension_it->second);
|
||||
// Don't add the extension to the enabled vector multiple times if
|
||||
// provided by the implementation itself or by another layer.
|
||||
if (!*requested_extension_it->second) {
|
||||
enabled_extensions.emplace_back(
|
||||
requested_extension_it->first.c_str());
|
||||
enabled_layer_extension_enablement_bools.push_back(
|
||||
requested_layer_it->second);
|
||||
*requested_extension_it->second = true;
|
||||
}
|
||||
}
|
||||
|
||||
assert_not_null(requested_layer_it->second);
|
||||
if (!*requested_layer_it->second) {
|
||||
enabled_layers.emplace_back(requested_layer_it->first.c_str());
|
||||
*requested_layer_it->second = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the instance.
|
||||
|
||||
VkApplicationInfo application_info;
|
||||
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
application_info.pNext = nullptr;
|
||||
application_info.pApplicationName = "Xenia";
|
||||
application_info.applicationVersion = 1;
|
||||
application_info.pEngineName = nullptr;
|
||||
application_info.engineVersion = 0;
|
||||
// "The patch version number specified in apiVersion is ignored when creating
|
||||
// an instance object."
|
||||
// "Vulkan 1.0 implementations were required to return
|
||||
// VK_ERROR_INCOMPATIBLE_DRIVER if apiVersion was larger than 1.0."
|
||||
application_info.apiVersion =
|
||||
vulkan_instance->api_version_ >= VK_MAKE_API_VERSION(0, 1, 1, 0)
|
||||
? VulkanDevice::kHighestUsedApiMinorVersion
|
||||
: VK_MAKE_API_VERSION(0, 1, 0, 0);
|
||||
|
||||
VkInstanceCreateInfo instance_create_info;
|
||||
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instance_create_info.pNext = nullptr;
|
||||
instance_create_info.flags = 0;
|
||||
// VK_KHR_get_physical_device_properties2 is needed to get the portability
|
||||
// subset features.
|
||||
if (vulkan_instance->extensions_.ext_KHR_portability_enumeration &&
|
||||
vulkan_instance->extensions_
|
||||
.ext_1_1_KHR_get_physical_device_properties2) {
|
||||
instance_create_info.flags |=
|
||||
VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
}
|
||||
instance_create_info.pApplicationInfo = &application_info;
|
||||
instance_create_info.enabledLayerCount = uint32_t(enabled_layers.size());
|
||||
instance_create_info.ppEnabledLayerNames = enabled_layers.data();
|
||||
instance_create_info.enabledExtensionCount =
|
||||
uint32_t(enabled_extensions.size());
|
||||
instance_create_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
VkResult instance_create_result = ifn.vkCreateInstance(
|
||||
&instance_create_info, nullptr, &vulkan_instance->instance_);
|
||||
|
||||
if (instance_create_result == VK_ERROR_LAYER_NOT_PRESENT ||
|
||||
instance_create_result == VK_ERROR_EXTENSION_NOT_PRESENT) {
|
||||
// A layer was possibly removed. Try without layers.
|
||||
for (bool* const extension_enablement :
|
||||
enabled_layer_extension_enablement_bools) {
|
||||
*extension_enablement = false;
|
||||
}
|
||||
for (const std::pair<std::string, bool*>& requested_layer :
|
||||
requested_layers) {
|
||||
*requested_layer.second = false;
|
||||
}
|
||||
instance_create_info.enabledLayerCount = 0;
|
||||
instance_create_info.enabledExtensionCount =
|
||||
uint32_t(enabled_implementation_extension_count);
|
||||
instance_create_result = ifn.vkCreateInstance(
|
||||
&instance_create_info, nullptr, &vulkan_instance->instance_);
|
||||
}
|
||||
|
||||
if (instance_create_result != VK_SUCCESS) {
|
||||
XELOGE("Failed to create a Vulkan instance: {}",
|
||||
vk::to_string(vk::Result(instance_create_result)));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Load instance functions.
|
||||
|
||||
#define XE_UI_VULKAN_FUNCTION(name) \
|
||||
functions_loaded &= (ifn.name = PFN_##name(ifn.vkGetInstanceProcAddr( \
|
||||
vulkan_instance->instance_, #name))) != nullptr;
|
||||
|
||||
// Vulkan 1.0.
|
||||
#include "xenia/ui/vulkan/functions/instance_1_0.inc"
|
||||
|
||||
// Extensions promoted to a Vulkan version supported by the instance.
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
functions_loaded &= \
|
||||
(ifn.core_name = PFN_##core_name(ifn.vkGetInstanceProcAddr( \
|
||||
vulkan_instance->instance_, #core_name))) != nullptr;
|
||||
if (vulkan_instance->api_version_ >= VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
#include "xenia/ui/vulkan/functions/instance_1_1_khr_get_physical_device_properties2.inc"
|
||||
}
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
|
||||
// Non-promoted extensions, and extensions promoted to a Vulkan version not
|
||||
// supported by the instance.
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
functions_loaded &= \
|
||||
(ifn.core_name = PFN_##core_name(ifn.vkGetInstanceProcAddr( \
|
||||
vulkan_instance->instance_, #extension_name))) != nullptr;
|
||||
if (vulkan_instance->api_version_ < VK_MAKE_API_VERSION(0, 1, 1, 0)) {
|
||||
if (vulkan_instance->extensions_
|
||||
.ext_1_1_KHR_get_physical_device_properties2) {
|
||||
#include "xenia/ui/vulkan/functions/instance_1_1_khr_get_physical_device_properties2.inc"
|
||||
}
|
||||
}
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
if (vulkan_instance->extensions_.ext_KHR_xcb_surface) {
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc"
|
||||
}
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||
if (vulkan_instance->extensions_.ext_KHR_android_surface) {
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc"
|
||||
}
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
if (vulkan_instance->extensions_.ext_KHR_win32_surface) {
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc"
|
||||
}
|
||||
#endif
|
||||
if (vulkan_instance->extensions_.ext_KHR_surface) {
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_surface.inc"
|
||||
}
|
||||
if (vulkan_instance->extensions_.ext_EXT_debug_utils) {
|
||||
#include "xenia/ui/vulkan/functions/instance_ext_debug_utils.inc"
|
||||
}
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
|
||||
#undef XE_UI_VULKAN_FUNCTION
|
||||
|
||||
if (!functions_loaded) {
|
||||
XELOGE("Failed to get all Vulkan instance function pointers");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check whether a surface can be created.
|
||||
|
||||
if (with_surface && !VulkanPresenter::GetSurfaceTypesSupportedByInstance(
|
||||
vulkan_instance->extensions_)) {
|
||||
XELOGE("The Vulkan instance doesn't support surface types used by Xenia");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Log instance properties.
|
||||
|
||||
XELOGI("Vulkan instance API version {}.{}.{}. Enabled layers and extensions:",
|
||||
VK_VERSION_MAJOR(vulkan_instance->api_version_),
|
||||
VK_VERSION_MINOR(vulkan_instance->api_version_),
|
||||
VK_VERSION_PATCH(vulkan_instance->api_version_));
|
||||
for (uint32_t enabled_layer_index = 0;
|
||||
enabled_layer_index < instance_create_info.enabledLayerCount;
|
||||
++enabled_layer_index) {
|
||||
XELOGI("* {}",
|
||||
instance_create_info.ppEnabledLayerNames[enabled_layer_index]);
|
||||
}
|
||||
for (uint32_t enabled_extension_index = 0;
|
||||
enabled_extension_index < instance_create_info.enabledExtensionCount;
|
||||
++enabled_extension_index) {
|
||||
XELOGI(
|
||||
"* {}",
|
||||
instance_create_info.ppEnabledExtensionNames[enabled_extension_index]);
|
||||
}
|
||||
|
||||
// Create the debug messenger if requested and available.
|
||||
|
||||
if (vulkan_instance->extensions_.ext_EXT_debug_utils &&
|
||||
cvars::vulkan_log_debug_messages) {
|
||||
VkDebugUtilsMessengerCreateInfoEXT debug_utils_messenger_create_info = {
|
||||
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
|
||||
if (xe::logging::ShouldLog(xe::LogLevel::Debug)) {
|
||||
debug_utils_messenger_create_info.messageSeverity |=
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
|
||||
}
|
||||
if (xe::logging::ShouldLog(xe::LogLevel::Info)) {
|
||||
debug_utils_messenger_create_info.messageSeverity |=
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
|
||||
}
|
||||
if (xe::logging::ShouldLog(xe::LogLevel::Warning)) {
|
||||
debug_utils_messenger_create_info.messageSeverity |=
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
|
||||
}
|
||||
if (xe::logging::ShouldLog(xe::LogLevel::Error)) {
|
||||
debug_utils_messenger_create_info.messageSeverity |=
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
}
|
||||
// VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-requiredbitmask:
|
||||
// "messageSeverity must not be 0"
|
||||
if (debug_utils_messenger_create_info.messageSeverity) {
|
||||
debug_utils_messenger_create_info.messageType =
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
debug_utils_messenger_create_info.pfnUserCallback =
|
||||
DebugUtilsMessengerCallback;
|
||||
debug_utils_messenger_create_info.pUserData = vulkan_instance.get();
|
||||
const VkResult debug_utils_messenger_create_result =
|
||||
ifn.vkCreateDebugUtilsMessengerEXT(
|
||||
vulkan_instance->instance_, &debug_utils_messenger_create_info,
|
||||
nullptr, &vulkan_instance->debug_utils_messenger_);
|
||||
if (debug_utils_messenger_create_result != VK_SUCCESS) {
|
||||
XELOGW("Failed to create the Vulkan debug utils messenger: {}",
|
||||
vk::to_string(vk::Result(debug_utils_messenger_create_result)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vulkan_instance;
|
||||
}
|
||||
|
||||
VulkanInstance::~VulkanInstance() {
|
||||
if (instance_) {
|
||||
if (debug_utils_messenger_ != VK_NULL_HANDLE) {
|
||||
functions_.vkDestroyDebugUtilsMessengerEXT(
|
||||
instance_, debug_utils_messenger_, nullptr);
|
||||
}
|
||||
|
||||
functions_.vkDestroyInstance(instance_, nullptr);
|
||||
}
|
||||
|
||||
#if XE_PLATFORM_LINUX
|
||||
if (loader_) {
|
||||
dlclose(loader_);
|
||||
}
|
||||
#elif XE_PLATFORM_WIN32
|
||||
if (loader_) {
|
||||
FreeLibrary(loader_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanInstance::EnumeratePhysicalDevices(
|
||||
std::vector<VkPhysicalDevice>& physical_devices_out) const {
|
||||
physical_devices_out.clear();
|
||||
while (true) {
|
||||
uint32_t physical_device_count = 0;
|
||||
const VkResult get_physical_device_count_result =
|
||||
functions_.vkEnumeratePhysicalDevices(instance_, &physical_device_count,
|
||||
nullptr);
|
||||
if ((get_physical_device_count_result != VK_SUCCESS &&
|
||||
get_physical_device_count_result != VK_INCOMPLETE) ||
|
||||
!physical_device_count) {
|
||||
return;
|
||||
}
|
||||
physical_devices_out.resize(physical_device_count);
|
||||
const VkResult get_physical_devices_result =
|
||||
functions_.vkEnumeratePhysicalDevices(instance_, &physical_device_count,
|
||||
physical_devices_out.data());
|
||||
if (get_physical_devices_result == VK_INCOMPLETE) {
|
||||
continue;
|
||||
}
|
||||
physical_devices_out.resize(
|
||||
get_physical_devices_result == VK_SUCCESS ? physical_device_count : 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VkBool32 VulkanInstance::DebugUtilsMessengerCallback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_types,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
||||
[[maybe_unused]] void* user_data) {
|
||||
xe::LogLevel log_level;
|
||||
char log_prefix_char;
|
||||
if (message_severity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
log_level = xe::LogLevel::Error;
|
||||
log_prefix_char = xe::logging::kPrefixCharError;
|
||||
} else if (message_severity >=
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||
log_level = xe::LogLevel::Warning;
|
||||
log_prefix_char = xe::logging::kPrefixCharWarning;
|
||||
} else if (message_severity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
|
||||
log_level = xe::LogLevel::Info;
|
||||
log_prefix_char = xe::logging::kPrefixCharInfo;
|
||||
} else {
|
||||
log_level = xe::LogLevel::Debug;
|
||||
log_prefix_char = xe::logging::kPrefixCharDebug;
|
||||
}
|
||||
|
||||
std::ostringstream log_str;
|
||||
|
||||
log_str << "Vulkan "
|
||||
<< vk::to_string(
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT(message_severity))
|
||||
<< " ("
|
||||
<< vk::to_string(vk::DebugUtilsMessageTypeFlagsEXT(message_types))
|
||||
<< ", ID " << callback_data->messageIdNumber;
|
||||
if (callback_data->pMessageIdName) {
|
||||
log_str << ": " << callback_data->pMessageIdName;
|
||||
}
|
||||
log_str << ')';
|
||||
|
||||
if (callback_data->pMessage) {
|
||||
log_str << ": " << callback_data->pMessage;
|
||||
}
|
||||
|
||||
bool annotations_begun = false;
|
||||
const auto begin_annotation = [&log_str, &annotations_begun]() {
|
||||
log_str << (annotations_begun ? ", " : " (");
|
||||
annotations_begun = true;
|
||||
};
|
||||
|
||||
for (uint32_t queue_label_index = 0;
|
||||
queue_label_index < callback_data->queueLabelCount;
|
||||
++queue_label_index) {
|
||||
begin_annotation();
|
||||
log_str << "queue label " << queue_label_index << ": "
|
||||
<< callback_data->pQueueLabels[queue_label_index].pLabelName;
|
||||
}
|
||||
|
||||
for (uint32_t cmd_buf_label_index = 0;
|
||||
cmd_buf_label_index < callback_data->cmdBufLabelCount;
|
||||
++cmd_buf_label_index) {
|
||||
begin_annotation();
|
||||
log_str << "command buffer label " << cmd_buf_label_index << ": "
|
||||
<< callback_data->pCmdBufLabels[cmd_buf_label_index].pLabelName;
|
||||
}
|
||||
|
||||
for (uint32_t object_index = 0; object_index < callback_data->objectCount;
|
||||
++object_index) {
|
||||
begin_annotation();
|
||||
const VkDebugUtilsObjectNameInfoEXT& object_info =
|
||||
callback_data->pObjects[object_index];
|
||||
// Lowercase hexadecimal digits in the handle to match the default Vulkan
|
||||
// debug utils messenger.
|
||||
log_str << "object " << object_index << ": "
|
||||
<< vk::to_string(vk::ObjectType(object_info.objectType)) << " 0x"
|
||||
<< std::hex << object_info.objectHandle << std::dec;
|
||||
if (object_info.pObjectName) {
|
||||
log_str << " '" << object_info.pObjectName << '\'';
|
||||
}
|
||||
}
|
||||
|
||||
if (annotations_begun) {
|
||||
log_str << ')';
|
||||
}
|
||||
|
||||
xe::logging::AppendLogLine(log_level, log_prefix_char, log_str.str());
|
||||
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
145
src/xenia/ui/vulkan/vulkan_instance.h
Normal file
145
src/xenia/ui/vulkan/vulkan_instance.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_UI_VULKAN_VULKAN_INSTANCE_H_
|
||||
#define XENIA_UI_VULKAN_VULKAN_INSTANCE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/ui/renderdoc_api.h"
|
||||
#include "xenia/ui/vulkan/vulkan_api.h"
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
class VulkanInstance {
|
||||
public:
|
||||
static std::unique_ptr<VulkanInstance> Create(bool with_surface,
|
||||
bool try_enable_validation);
|
||||
|
||||
VulkanInstance(const VulkanInstance&) = delete;
|
||||
VulkanInstance& operator=(const VulkanInstance&) = delete;
|
||||
VulkanInstance(VulkanInstance&&) = delete;
|
||||
VulkanInstance& operator=(VulkanInstance&&) = delete;
|
||||
|
||||
~VulkanInstance();
|
||||
|
||||
// nullptr if RenderDoc is not connected.
|
||||
RenderDocAPI* renderdoc_api() const { return renderdoc_api_.get(); }
|
||||
|
||||
struct Functions {
|
||||
// From the loader module.
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr;
|
||||
PFN_vkDestroyInstance vkDestroyInstance = nullptr;
|
||||
|
||||
// From vkGetInstanceProcAddr for nullptr.
|
||||
PFN_vkCreateInstance vkCreateInstance = nullptr;
|
||||
PFN_vkEnumerateInstanceExtensionProperties
|
||||
vkEnumerateInstanceExtensionProperties = nullptr;
|
||||
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties =
|
||||
nullptr;
|
||||
// Vulkan 1.1.
|
||||
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion = nullptr;
|
||||
|
||||
// From vkGetInstanceProcAddr for the instance.
|
||||
#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name = nullptr;
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
PFN_##core_name core_name = nullptr;
|
||||
#include "xenia/ui/vulkan/functions/instance_1_0.inc"
|
||||
// VK_KHR_surface (#1)
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_surface.inc"
|
||||
// VK_KHR_xcb_surface (#6)
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc"
|
||||
#endif
|
||||
// VK_KHR_android_surface (#9)
|
||||
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc"
|
||||
#endif
|
||||
// VK_KHR_win32_surface (#10)
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc"
|
||||
#endif
|
||||
// VK_KHR_get_physical_device_properties2 (#60, promoted to 1.1)
|
||||
#include "xenia/ui/vulkan/functions/instance_1_1_khr_get_physical_device_properties2.inc"
|
||||
// VK_EXT_debug_utils (#129)
|
||||
#include "xenia/ui/vulkan/functions/instance_ext_debug_utils.inc"
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
#undef XE_UI_VULKAN_FUNCTION
|
||||
};
|
||||
|
||||
const Functions& functions() const { return functions_; }
|
||||
|
||||
uint32_t api_version() const { return api_version_; }
|
||||
|
||||
// Also set to true if the version of the Vulkan API they were promoted to it
|
||||
// supported (with the `ext_major_minor_` prefix rather than `ext_`).
|
||||
struct Extensions {
|
||||
bool ext_KHR_surface = false; // #1
|
||||
#ifdef VK_USE_PLATFORM_XCB_KHR
|
||||
bool ext_KHR_xcb_surface = false; // #6
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||
bool ext_KHR_android_surface = false; // #9
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
bool ext_KHR_win32_surface = false; // #10
|
||||
#endif
|
||||
bool ext_1_1_KHR_get_physical_device_properties2 = false; // #60
|
||||
bool ext_EXT_debug_utils = false; // #129
|
||||
bool ext_KHR_portability_enumeration = false; // #395
|
||||
};
|
||||
|
||||
const Extensions& extensions() const { return extensions_; }
|
||||
|
||||
VkInstance instance() const { return instance_; }
|
||||
|
||||
void EnumeratePhysicalDevices(
|
||||
std::vector<VkPhysicalDevice>& physical_devices_out) const;
|
||||
|
||||
private:
|
||||
explicit VulkanInstance() = default;
|
||||
|
||||
std::unique_ptr<RenderDocAPI> renderdoc_api_;
|
||||
|
||||
#if XE_PLATFORM_LINUX
|
||||
void* loader_ = nullptr;
|
||||
#elif XE_PLATFORM_WIN32
|
||||
HMODULE loader_ = nullptr;
|
||||
#endif
|
||||
|
||||
Functions functions_;
|
||||
|
||||
uint32_t api_version_ = VK_MAKE_API_VERSION(0, 1, 0, 0);
|
||||
|
||||
Extensions extensions_;
|
||||
|
||||
VkInstance instance_ = nullptr;
|
||||
|
||||
static VkBool32 DebugUtilsMessengerCallback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_types,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
||||
void* user_data);
|
||||
|
||||
VkDebugUtilsMessengerEXT debug_utils_messenger_ = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_UI_VULKAN_VULKAN_INSTANCE_H_
|
||||
|
|
@ -14,25 +14,24 @@
|
|||
#include <cstring>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
VmaAllocator CreateVmaAllocator(const VulkanProvider& provider,
|
||||
bool externally_synchronized) {
|
||||
const VulkanProvider::LibraryFunctions& lfn = provider.lfn();
|
||||
const VulkanProvider::InstanceFunctions& ifn = provider.ifn();
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
const VulkanProvider::InstanceExtensions& instance_extensions =
|
||||
provider.instance_extensions();
|
||||
const VulkanProvider::DeviceInfo& device_info = provider.device_info();
|
||||
VmaAllocator CreateVmaAllocator(const VulkanDevice* const vulkan_device,
|
||||
const bool externally_synchronized) {
|
||||
assert_not_null(vulkan_device);
|
||||
|
||||
const VulkanInstance* const vulkan_instance =
|
||||
vulkan_device->vulkan_instance();
|
||||
const VulkanInstance::Functions& ifn = vulkan_instance->functions();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
|
||||
VmaVulkanFunctions vma_vulkan_functions = {};
|
||||
VmaAllocatorCreateInfo allocator_create_info = {};
|
||||
|
||||
vma_vulkan_functions.vkGetInstanceProcAddr = lfn.vkGetInstanceProcAddr;
|
||||
vma_vulkan_functions.vkGetInstanceProcAddr = ifn.vkGetInstanceProcAddr;
|
||||
vma_vulkan_functions.vkGetDeviceProcAddr = ifn.vkGetDeviceProcAddr;
|
||||
vma_vulkan_functions.vkGetPhysicalDeviceProperties =
|
||||
ifn.vkGetPhysicalDeviceProperties;
|
||||
|
|
@ -57,29 +56,30 @@ VmaAllocator CreateVmaAllocator(const VulkanProvider& provider,
|
|||
vma_vulkan_functions.vkCreateImage = dfn.vkCreateImage;
|
||||
vma_vulkan_functions.vkDestroyImage = dfn.vkDestroyImage;
|
||||
vma_vulkan_functions.vkCmdCopyBuffer = dfn.vkCmdCopyBuffer;
|
||||
if (device_info.ext_1_1_VK_KHR_get_memory_requirements2) {
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_get_memory_requirements2) {
|
||||
vma_vulkan_functions.vkGetBufferMemoryRequirements2KHR =
|
||||
dfn.vkGetBufferMemoryRequirements2;
|
||||
vma_vulkan_functions.vkGetImageMemoryRequirements2KHR =
|
||||
dfn.vkGetImageMemoryRequirements2;
|
||||
if (device_info.ext_1_1_VK_KHR_dedicated_allocation) {
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_dedicated_allocation) {
|
||||
allocator_create_info.flags |=
|
||||
VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
|
||||
}
|
||||
}
|
||||
if (device_info.ext_1_1_VK_KHR_bind_memory2) {
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_bind_memory2) {
|
||||
vma_vulkan_functions.vkBindBufferMemory2KHR = dfn.vkBindBufferMemory2;
|
||||
vma_vulkan_functions.vkBindImageMemory2KHR = dfn.vkBindImageMemory2;
|
||||
allocator_create_info.flags |= VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT;
|
||||
}
|
||||
if (instance_extensions.khr_get_physical_device_properties2) {
|
||||
if (vulkan_instance->extensions()
|
||||
.ext_1_1_KHR_get_physical_device_properties2) {
|
||||
vma_vulkan_functions.vkGetPhysicalDeviceMemoryProperties2KHR =
|
||||
ifn.vkGetPhysicalDeviceMemoryProperties2;
|
||||
if (device_info.ext_VK_EXT_memory_budget) {
|
||||
if (vulkan_device->extensions().ext_EXT_memory_budget) {
|
||||
allocator_create_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
|
||||
}
|
||||
}
|
||||
if (device_info.ext_1_3_VK_KHR_maintenance4) {
|
||||
if (vulkan_device->extensions().ext_1_3_KHR_maintenance4) {
|
||||
vma_vulkan_functions.vkGetDeviceBufferMemoryRequirements =
|
||||
dfn.vkGetDeviceBufferMemoryRequirements;
|
||||
vma_vulkan_functions.vkGetDeviceImageMemoryRequirements =
|
||||
|
|
@ -90,11 +90,12 @@ VmaAllocator CreateVmaAllocator(const VulkanProvider& provider,
|
|||
allocator_create_info.flags |=
|
||||
VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
|
||||
}
|
||||
allocator_create_info.physicalDevice = provider.physical_device();
|
||||
allocator_create_info.device = provider.device();
|
||||
allocator_create_info.physicalDevice = vulkan_device->physical_device();
|
||||
allocator_create_info.device = vulkan_device->device();
|
||||
allocator_create_info.pVulkanFunctions = &vma_vulkan_functions;
|
||||
allocator_create_info.instance = provider.instance();
|
||||
allocator_create_info.vulkanApiVersion = device_info.apiVersion;
|
||||
allocator_create_info.instance = vulkan_instance->instance();
|
||||
allocator_create_info.vulkanApiVersion =
|
||||
vulkan_device->properties().apiVersion;
|
||||
VmaAllocator allocator;
|
||||
if (vmaCreateAllocator(&allocator_create_info, &allocator) != VK_SUCCESS) {
|
||||
XELOGE("Failed to create a Vulkan Memory Allocator instance");
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
// Make sure vulkan.h is included from third_party (rather than from the system
|
||||
// include directory) before vk_mem_alloc.h.
|
||||
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
||||
// Work around the pointer nullability completeness warnings on Clang.
|
||||
|
|
@ -29,7 +29,7 @@ namespace xe {
|
|||
namespace ui {
|
||||
namespace vulkan {
|
||||
|
||||
VmaAllocator CreateVmaAllocator(const VulkanProvider& provider,
|
||||
VmaAllocator CreateVmaAllocator(const VulkanDevice* vulkan_device,
|
||||
bool externally_synchronized);
|
||||
|
||||
} // namespace vulkan
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ namespace shaders {
|
|||
} // namespace shaders
|
||||
|
||||
VulkanPresenter::PaintContext::Submission::~Submission() {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
if (draw_command_pool_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyCommandPool(device, draw_command_pool_, nullptr);
|
||||
|
|
@ -89,8 +89,8 @@ VulkanPresenter::PaintContext::Submission::~Submission() {
|
|||
}
|
||||
|
||||
bool VulkanPresenter::PaintContext::Submission::Initialize() {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkSemaphoreCreateInfo semaphore_create_info;
|
||||
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
|
|
@ -116,7 +116,7 @@ bool VulkanPresenter::PaintContext::Submission::Initialize() {
|
|||
command_pool_create_info.pNext = nullptr;
|
||||
command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
command_pool_create_info.queueFamilyIndex =
|
||||
provider_.queue_family_graphics_compute();
|
||||
vulkan_device_->queue_family_graphics_compute();
|
||||
if (dfn.vkCreateCommandPool(device, &command_pool_create_info, nullptr,
|
||||
&draw_command_pool_) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
|
|
@ -161,8 +161,8 @@ VulkanPresenter::~VulkanPresenter() {
|
|||
ui_submission_tracker_.Shutdown();
|
||||
guest_output_image_refresher_submission_tracker_.Shutdown();
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
if (paint_context_.swapchain_render_pass != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyRenderPass(device, paint_context_.swapchain_render_pass,
|
||||
|
|
@ -207,11 +207,36 @@ VulkanPresenter::~VulkanPresenter() {
|
|||
guest_output_paint_image_descriptor_set_layout_);
|
||||
}
|
||||
|
||||
Surface::TypeFlags VulkanPresenter::GetSupportedSurfaceTypes() const {
|
||||
if (!provider_.device_info().ext_VK_KHR_swapchain) {
|
||||
Surface::TypeFlags VulkanPresenter::GetSurfaceTypesSupportedByInstance(
|
||||
const VulkanInstance::Extensions& instance_extensions) {
|
||||
if (!instance_extensions.ext_KHR_surface) {
|
||||
return 0;
|
||||
}
|
||||
return GetSurfaceTypesSupportedByInstance(provider_.instance_extensions());
|
||||
Surface::TypeFlags type_flags = 0;
|
||||
#if XE_PLATFORM_ANDROID
|
||||
if (instance_extensions.ext_KHR_android_surface) {
|
||||
type_flags |= Surface::kTypeFlag_AndroidNativeWindow;
|
||||
}
|
||||
#endif
|
||||
#if XE_PLATFORM_GNU_LINUX
|
||||
if (instance_extensions.ext_KHR_xcb_surface) {
|
||||
type_flags |= Surface::kTypeFlag_XcbWindow;
|
||||
}
|
||||
#endif
|
||||
#if XE_PLATFORM_WIN32
|
||||
if (instance_extensions.ext_KHR_win32_surface) {
|
||||
type_flags |= Surface::kTypeFlag_Win32Hwnd;
|
||||
}
|
||||
#endif
|
||||
return type_flags;
|
||||
}
|
||||
|
||||
Surface::TypeFlags VulkanPresenter::GetSupportedSurfaceTypes() const {
|
||||
if (!vulkan_device_->extensions().ext_KHR_swapchain) {
|
||||
return 0;
|
||||
}
|
||||
return GetSurfaceTypesSupportedByInstance(
|
||||
vulkan_device_->vulkan_instance()->extensions());
|
||||
}
|
||||
|
||||
bool VulkanPresenter::CaptureGuestOutput(RawImage& image_out) {
|
||||
|
|
@ -239,14 +264,14 @@ bool VulkanPresenter::CaptureGuestOutput(RawImage& image_out) {
|
|||
VkBuffer buffer;
|
||||
VkDeviceMemory buffer_memory;
|
||||
if (!util::CreateDedicatedAllocationBuffer(
|
||||
provider_, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
vulkan_device_, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
util::MemoryPurpose::kReadback, buffer, buffer_memory)) {
|
||||
XELOGE("VulkanPresenter: Failed to create the guest output capture buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
{
|
||||
VkCommandPoolCreateInfo command_pool_create_info;
|
||||
|
|
@ -254,7 +279,7 @@ bool VulkanPresenter::CaptureGuestOutput(RawImage& image_out) {
|
|||
command_pool_create_info.pNext = nullptr;
|
||||
command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
command_pool_create_info.queueFamilyIndex =
|
||||
provider_.queue_family_graphics_compute();
|
||||
vulkan_device_->queue_family_graphics_compute();
|
||||
VkCommandPool command_pool;
|
||||
if (dfn.vkCreateCommandPool(device, &command_pool_create_info, nullptr,
|
||||
&command_pool) != VK_SUCCESS) {
|
||||
|
|
@ -361,7 +386,7 @@ bool VulkanPresenter::CaptureGuestOutput(RawImage& image_out) {
|
|||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &command_buffer;
|
||||
VulkanSubmissionTracker submission_tracker(provider_);
|
||||
VulkanSubmissionTracker submission_tracker(vulkan_device_);
|
||||
{
|
||||
VulkanSubmissionTracker::FenceAcquisition fence_acqusition(
|
||||
submission_tracker.AcquireFenceToAdvanceSubmission());
|
||||
|
|
@ -377,11 +402,12 @@ bool VulkanPresenter::CaptureGuestOutput(RawImage& image_out) {
|
|||
}
|
||||
VkResult submit_result;
|
||||
{
|
||||
VulkanProvider::QueueAcquisition queue_acquisition(
|
||||
provider_.AcquireQueue(provider_.queue_family_graphics_compute(),
|
||||
0));
|
||||
submit_result = dfn.vkQueueSubmit(
|
||||
queue_acquisition.queue, 1, &submit_info, fence_acqusition.fence());
|
||||
const VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device_->AcquireQueue(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0);
|
||||
submit_result =
|
||||
dfn.vkQueueSubmit(queue_acquisition.queue(), 1, &submit_info,
|
||||
fence_acqusition.fence());
|
||||
}
|
||||
if (submit_result != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
|
|
@ -442,8 +468,8 @@ VkCommandBuffer VulkanPresenter::AcquireUISetupCommandBufferFromUIThread() {
|
|||
.command_buffer;
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkCommandBufferBeginInfo command_buffer_begin_info;
|
||||
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
|
|
@ -488,7 +514,7 @@ VkCommandBuffer VulkanPresenter::AcquireUISetupCommandBufferFromUIThread() {
|
|||
command_pool_create_info.pNext = nullptr;
|
||||
command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
command_pool_create_info.queueFamilyIndex =
|
||||
provider_.queue_family_graphics_compute();
|
||||
vulkan_device_->queue_family_graphics_compute();
|
||||
VkCommandPool new_command_pool;
|
||||
if (dfn.vkCreateCommandPool(device, &command_pool_create_info, nullptr,
|
||||
&new_command_pool) != VK_SUCCESS) {
|
||||
|
|
@ -529,11 +555,12 @@ VulkanPresenter::ConnectOrReconnectPaintingToSurfaceFromUIThread(
|
|||
Surface& new_surface, uint32_t new_surface_width,
|
||||
uint32_t new_surface_height, bool was_paintable,
|
||||
bool& is_vsync_implicit_out) {
|
||||
const VulkanProvider::InstanceFunctions& ifn = provider_.ifn();
|
||||
VkInstance instance = provider_.instance();
|
||||
VkPhysicalDevice physical_device = provider_.physical_device();
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanInstance* const vulkan_instance =
|
||||
vulkan_device_->vulkan_instance();
|
||||
const VulkanInstance::Functions& ifn = vulkan_instance->functions();
|
||||
const VkInstance instance = vulkan_instance->instance();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkFormat new_swapchain_format;
|
||||
|
||||
|
|
@ -550,7 +577,7 @@ VulkanPresenter::ConnectOrReconnectPaintingToSurfaceFromUIThread(
|
|||
paint_context_.PrepareForSwapchainRetirement();
|
||||
bool surface_unusable;
|
||||
paint_context_.swapchain = PaintContext::CreateSwapchainForVulkanSurface(
|
||||
provider_, paint_context_.vulkan_surface, new_surface_width,
|
||||
vulkan_device_, paint_context_.vulkan_surface, new_surface_width,
|
||||
new_surface_height, old_swapchain, paint_context_.present_queue_family,
|
||||
new_swapchain_format, paint_context_.swapchain_extent,
|
||||
paint_context_.swapchain_is_fifo, surface_unusable);
|
||||
|
|
@ -641,7 +668,7 @@ VulkanPresenter::ConnectOrReconnectPaintingToSurfaceFromUIThread(
|
|||
}
|
||||
bool surface_unusable;
|
||||
paint_context_.swapchain = PaintContext::CreateSwapchainForVulkanSurface(
|
||||
provider_, paint_context_.vulkan_surface, new_surface_width,
|
||||
vulkan_device_, paint_context_.vulkan_surface, new_surface_width,
|
||||
new_surface_height, VK_NULL_HANDLE, paint_context_.present_queue_family,
|
||||
new_swapchain_format, paint_context_.swapchain_extent,
|
||||
paint_context_.swapchain_is_fifo, surface_unusable);
|
||||
|
|
@ -837,7 +864,7 @@ bool VulkanPresenter::RefreshGuestOutputImpl(
|
|||
assert_not_zero(frontbuffer_width);
|
||||
assert_not_zero(frontbuffer_height);
|
||||
VkExtent2D max_framebuffer_extent =
|
||||
util::GetMax2DFramebufferExtent(provider_);
|
||||
util::GetMax2DFramebufferExtent(vulkan_device_->properties());
|
||||
if (frontbuffer_width > max_framebuffer_extent.width ||
|
||||
frontbuffer_height > max_framebuffer_extent.height) {
|
||||
// Writing the guest output isn't supposed to rescale, and a guest texture
|
||||
|
|
@ -856,7 +883,7 @@ bool VulkanPresenter::RefreshGuestOutputImpl(
|
|||
}
|
||||
if (!image_instance.image) {
|
||||
std::unique_ptr<GuestOutputImage> new_image = GuestOutputImage::Create(
|
||||
provider_, frontbuffer_width, frontbuffer_height);
|
||||
vulkan_device_, frontbuffer_width, frontbuffer_height);
|
||||
if (!new_image) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -883,14 +910,15 @@ bool VulkanPresenter::RefreshGuestOutputImpl(
|
|||
// "Fence signal operations that are defined by vkQueueSubmit additionally
|
||||
// include in the first synchronization scope all commands that occur earlier
|
||||
// in submission order."
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
{
|
||||
VulkanSubmissionTracker::FenceAcquisition fence_acqusition(
|
||||
guest_output_image_refresher_submission_tracker_
|
||||
.AcquireFenceToAdvanceSubmission());
|
||||
VulkanProvider::QueueAcquisition queue_acquisition(
|
||||
provider_.AcquireQueue(provider_.queue_family_graphics_compute(), 0));
|
||||
if (dfn.vkQueueSubmit(queue_acquisition.queue, 0, nullptr,
|
||||
const VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device_->AcquireQueue(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0);
|
||||
if (dfn.vkQueueSubmit(queue_acquisition.queue(), 0, nullptr,
|
||||
fence_acqusition.fence()) != VK_SUCCESS) {
|
||||
fence_acqusition.SubmissionSucceededSignalFailed();
|
||||
}
|
||||
|
|
@ -900,18 +928,18 @@ bool VulkanPresenter::RefreshGuestOutputImpl(
|
|||
}
|
||||
|
||||
VkSwapchainKHR VulkanPresenter::PaintContext::CreateSwapchainForVulkanSurface(
|
||||
const VulkanProvider& provider, VkSurfaceKHR surface, uint32_t width,
|
||||
const VulkanDevice* vulkan_device, VkSurfaceKHR surface, uint32_t width,
|
||||
uint32_t height, VkSwapchainKHR old_swapchain,
|
||||
uint32_t& present_queue_family_out, VkFormat& image_format_out,
|
||||
VkExtent2D& image_extent_out, bool& is_fifo_out,
|
||||
bool& ui_surface_unusable_out) {
|
||||
ui_surface_unusable_out = false;
|
||||
|
||||
const VulkanProvider::InstanceFunctions& ifn = provider.ifn();
|
||||
VkInstance instance = provider.instance();
|
||||
VkPhysicalDevice physical_device = provider.physical_device();
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const VulkanInstance::Functions& ifn =
|
||||
vulkan_device->vulkan_instance()->functions();
|
||||
const VkPhysicalDevice physical_device = vulkan_device->physical_device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
// Get surface capabilities.
|
||||
VkSurfaceCapabilitiesKHR surface_capabilities;
|
||||
|
|
@ -934,7 +962,8 @@ VkSwapchainKHR VulkanPresenter::PaintContext::CreateSwapchainForVulkanSurface(
|
|||
// requirements - the maximum 2D framebuffer size on the specific physical
|
||||
// device, and the minimum swap chain size on the whole instance - fail to
|
||||
// create until the surface becomes smaller).
|
||||
VkExtent2D max_framebuffer_extent = util::GetMax2DFramebufferExtent(provider);
|
||||
VkExtent2D max_framebuffer_extent =
|
||||
util::GetMax2DFramebufferExtent(vulkan_device->properties());
|
||||
VkExtent2D image_extent;
|
||||
image_extent.width =
|
||||
std::min(std::max(std::min(width, max_framebuffer_extent.width),
|
||||
|
|
@ -952,17 +981,17 @@ VkSwapchainKHR VulkanPresenter::PaintContext::CreateSwapchainForVulkanSurface(
|
|||
|
||||
// Get the queue family for presentation.
|
||||
uint32_t queue_family_index_present = UINT32_MAX;
|
||||
const std::vector<VulkanProvider::QueueFamily>& queue_families =
|
||||
provider.queue_families();
|
||||
const std::vector<VulkanDevice::QueueFamily>& queue_families =
|
||||
vulkan_device->queue_families();
|
||||
VkBool32 queue_family_present_supported;
|
||||
// First try the graphics and compute queue, prefer it to avoid the concurrent
|
||||
// image sharing mode.
|
||||
uint32_t queue_family_index_graphics_compute =
|
||||
provider.queue_family_graphics_compute();
|
||||
const VulkanProvider::QueueFamily& queue_family_graphics_compute =
|
||||
vulkan_device->queue_family_graphics_compute();
|
||||
const VulkanDevice::QueueFamily& queue_family_graphics_compute =
|
||||
queue_families[queue_family_index_graphics_compute];
|
||||
if (queue_family_graphics_compute.potentially_supports_present &&
|
||||
queue_family_graphics_compute.queue_count &&
|
||||
if (queue_family_graphics_compute.may_support_presentation &&
|
||||
!queue_family_graphics_compute.queues.empty() &&
|
||||
ifn.vkGetPhysicalDeviceSurfaceSupportKHR(
|
||||
physical_device, queue_family_index_graphics_compute, surface,
|
||||
&queue_family_present_supported) == VK_SUCCESS &&
|
||||
|
|
@ -970,9 +999,9 @@ VkSwapchainKHR VulkanPresenter::PaintContext::CreateSwapchainForVulkanSurface(
|
|||
queue_family_index_present = queue_family_index_graphics_compute;
|
||||
} else {
|
||||
for (uint32_t i = 0; i < uint32_t(queue_families.size()); ++i) {
|
||||
const VulkanProvider::QueueFamily& queue_family = queue_families[i];
|
||||
if (queue_family.potentially_supports_present &&
|
||||
queue_family.queue_count &&
|
||||
const VulkanDevice::QueueFamily& queue_family = queue_families[i];
|
||||
if (!queue_family.queues.empty() &&
|
||||
queue_family.may_support_presentation &&
|
||||
ifn.vkGetPhysicalDeviceSurfaceSupportKHR(
|
||||
physical_device, i, surface, &queue_family_present_supported) ==
|
||||
VK_SUCCESS &&
|
||||
|
|
@ -1228,7 +1257,7 @@ VkSwapchainKHR VulkanPresenter::PaintContext::CreateSwapchainForVulkanSurface(
|
|||
XELOGE("VulkanPresenter: Failed to create a swapchain");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
XELOGVK(
|
||||
XELOGI(
|
||||
"VulkanPresenter: Created {}x{} swapchain with format {}, color space "
|
||||
"{}, presentation mode {}",
|
||||
swapchain_create_info.imageExtent.width,
|
||||
|
|
@ -1250,8 +1279,8 @@ VkSwapchainKHR VulkanPresenter::PaintContext::PrepareForSwapchainRetirement() {
|
|||
if (swapchain != VK_NULL_HANDLE) {
|
||||
submission_tracker.AwaitAllSubmissionsCompletion();
|
||||
}
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
for (const SwapchainFramebuffer& framebuffer : swapchain_framebuffers) {
|
||||
dfn.vkDestroyFramebuffer(device, framebuffer.framebuffer, nullptr);
|
||||
dfn.vkDestroyImageView(device, framebuffer.image_view, nullptr);
|
||||
|
|
@ -1269,22 +1298,21 @@ VkSwapchainKHR VulkanPresenter::PaintContext::PrepareForSwapchainRetirement() {
|
|||
void VulkanPresenter::PaintContext::DestroySwapchainAndVulkanSurface() {
|
||||
VkSwapchainKHR old_swapchain = PrepareForSwapchainRetirement();
|
||||
if (old_swapchain != VK_NULL_HANDLE) {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
dfn.vkDestroySwapchainKHR(device, old_swapchain, nullptr);
|
||||
vulkan_device->functions().vkDestroySwapchainKHR(vulkan_device->device(),
|
||||
old_swapchain, nullptr);
|
||||
}
|
||||
present_queue_family = UINT32_MAX;
|
||||
if (vulkan_surface != VK_NULL_HANDLE) {
|
||||
const VulkanProvider::InstanceFunctions& ifn = provider.ifn();
|
||||
VkInstance instance = provider.instance();
|
||||
ifn.vkDestroySurfaceKHR(instance, vulkan_surface, nullptr);
|
||||
const VulkanInstance* vulkan_instance = vulkan_device->vulkan_instance();
|
||||
vulkan_instance->functions().vkDestroySurfaceKHR(
|
||||
vulkan_instance->instance(), vulkan_surface, nullptr);
|
||||
vulkan_surface = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
VulkanPresenter::GuestOutputImage::~GuestOutputImage() {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
if (view_ != VK_NULL_HANDLE) {
|
||||
dfn.vkDestroyImageView(device, view_, nullptr);
|
||||
}
|
||||
|
|
@ -1318,14 +1346,14 @@ bool VulkanPresenter::GuestOutputImage::Initialize() {
|
|||
image_create_info.pQueueFamilyIndices = nullptr;
|
||||
image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationImage(
|
||||
provider_, image_create_info,
|
||||
vulkan_device_, image_create_info,
|
||||
ui::vulkan::util::MemoryPurpose::kDeviceLocal, image_, memory_)) {
|
||||
XELOGE("VulkanPresenter: Failed to create a guest output image");
|
||||
return false;
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkImageViewCreateInfo image_view_create_info;
|
||||
image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
|
|
@ -1368,8 +1396,8 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
*paint_context_.submissions[current_paint_submission_index %
|
||||
paint_submission_count];
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkCommandPool draw_command_pool = paint_submission.draw_command_pool();
|
||||
if (dfn.vkResetCommandPool(device, draw_command_pool, 0) != VK_SUCCESS) {
|
||||
|
|
@ -1414,7 +1442,7 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
|
||||
// Not an error, reporting just as info (may normally occur while resizing
|
||||
// on some platforms).
|
||||
XELOGVK(
|
||||
XELOGI(
|
||||
"VulkanPresenter: Presentation to the swapchain image has been "
|
||||
"dropped as the swapchain or the surface has become outdated");
|
||||
return PaintResult::kNotPresentedConnectionOutdated;
|
||||
|
|
@ -1482,7 +1510,7 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
|
||||
if (guest_output_image) {
|
||||
VkExtent2D max_framebuffer_extent =
|
||||
util::GetMax2DFramebufferExtent(provider_);
|
||||
util::GetMax2DFramebufferExtent(vulkan_device_->properties());
|
||||
GuestOutputPaintFlow guest_output_flow = GetGuestOutputPaintFlow(
|
||||
guest_output_properties, paint_context_.swapchain_extent.width,
|
||||
paint_context_.swapchain_extent.height, max_framebuffer_extent.width,
|
||||
|
|
@ -1599,7 +1627,7 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
}
|
||||
// Image.
|
||||
intermediate_image_ptr_ref = GuestOutputImage::Create(
|
||||
provider_, intermediate_needed_size.first,
|
||||
vulkan_device_, intermediate_needed_size.first,
|
||||
intermediate_needed_size.second);
|
||||
if (!intermediate_image_ptr_ref) {
|
||||
// Don't display the guest output, and don't try to create more
|
||||
|
|
@ -2029,13 +2057,14 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
}
|
||||
VkResult submit_result;
|
||||
{
|
||||
VulkanProvider::QueueAcquisition queue_acquisition(
|
||||
provider_.AcquireQueue(provider_.queue_family_graphics_compute(), 0));
|
||||
submit_result = dfn.vkQueueSubmit(queue_acquisition.queue, 1,
|
||||
const VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device_->AcquireQueue(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0);
|
||||
submit_result = dfn.vkQueueSubmit(queue_acquisition.queue(), 1,
|
||||
&submit_info, fence_acqusition.fence());
|
||||
if (ui_fence_acquisition.fence() != VK_NULL_HANDLE &&
|
||||
submit_result == VK_SUCCESS) {
|
||||
if (dfn.vkQueueSubmit(queue_acquisition.queue, 0, nullptr,
|
||||
if (dfn.vkQueueSubmit(queue_acquisition.queue(), 0, nullptr,
|
||||
ui_fence_acquisition.fence()) != VK_SUCCESS) {
|
||||
ui_fence_acquisition.SubmissionSucceededSignalFailed();
|
||||
}
|
||||
|
|
@ -2072,10 +2101,10 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
present_info.pResults = nullptr;
|
||||
VkResult present_result;
|
||||
{
|
||||
VulkanProvider::QueueAcquisition queue_acquisition(
|
||||
provider_.AcquireQueue(paint_context_.present_queue_family, 0));
|
||||
const VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device_->AcquireQueue(paint_context_.present_queue_family, 0);
|
||||
present_result =
|
||||
dfn.vkQueuePresentKHR(queue_acquisition.queue, &present_info);
|
||||
dfn.vkQueuePresentKHR(queue_acquisition.queue(), &present_info);
|
||||
}
|
||||
switch (present_result) {
|
||||
case VK_SUCCESS:
|
||||
|
|
@ -2092,7 +2121,7 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
|
||||
// Not an error, reporting just as info (may normally occur while resizing
|
||||
// on some platforms).
|
||||
XELOGVK(
|
||||
XELOGI(
|
||||
"VulkanPresenter: Presentation to the swapchain image has been "
|
||||
"dropped as the swapchain or the surface has become outdated");
|
||||
// Note that the semaphore wait (followed by reset) has been enqueued,
|
||||
|
|
@ -2108,8 +2137,8 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
|||
}
|
||||
|
||||
bool VulkanPresenter::InitializeSurfaceIndependent() {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkDescriptorSetLayoutBinding guest_output_image_sampler_bindings[2];
|
||||
guest_output_image_sampler_bindings[0].binding = 0;
|
||||
|
|
@ -2119,8 +2148,8 @@ bool VulkanPresenter::InitializeSurfaceIndependent() {
|
|||
guest_output_image_sampler_bindings[0].stageFlags =
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
guest_output_image_sampler_bindings[0].pImmutableSamplers = nullptr;
|
||||
VkSampler sampler_linear_clamp =
|
||||
provider_.GetHostSampler(VulkanProvider::HostSampler::kLinearClamp);
|
||||
const VkSampler sampler_linear_clamp =
|
||||
ui_samplers_->samplers()[UISamplers::kSamplerIndexLinearClampToEdge];
|
||||
guest_output_image_sampler_bindings[1].binding = 1;
|
||||
guest_output_image_sampler_bindings[1].descriptorType =
|
||||
VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
|
|
@ -2371,7 +2400,8 @@ bool VulkanPresenter::InitializeSurfaceIndependent() {
|
|||
// Initialize connection-independent parts of the painting context.
|
||||
|
||||
for (size_t i = 0; i < paint_context_.submissions.size(); ++i) {
|
||||
paint_context_.submissions[i] = PaintContext::Submission::Create(provider_);
|
||||
paint_context_.submissions[i] =
|
||||
PaintContext::Submission::Create(vulkan_device_);
|
||||
if (!paint_context_.submissions[i]) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2536,8 +2566,8 @@ VkPipeline VulkanPresenter::CreateGuestOutputPaintPipeline(
|
|||
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
|
||||
pipeline_create_info.basePipelineIndex = -1;
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkPipeline pipeline;
|
||||
if (dfn.vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1,
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@
|
|||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/ui/presenter.h"
|
||||
#include "xenia/ui/surface.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/ui_samplers.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
#include "xenia/ui/vulkan/vulkan_instance.h"
|
||||
#include "xenia/ui/vulkan/vulkan_submission_tracker.h"
|
||||
|
||||
namespace xe {
|
||||
|
|
@ -90,7 +91,7 @@ class VulkanPresenter final : public Presenter {
|
|||
static constexpr VkImageLayout kGuestOutputInternalLayout =
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
// The callback must use the graphics and compute queue 0 of the provider.
|
||||
// The callback must use the graphics and compute queue 0 of the device.
|
||||
class VulkanGuestOutputRefreshContext final
|
||||
: public GuestOutputRefreshContext {
|
||||
public:
|
||||
|
|
@ -123,9 +124,10 @@ class VulkanPresenter final : public Presenter {
|
|||
};
|
||||
|
||||
static std::unique_ptr<VulkanPresenter> Create(
|
||||
HostGpuLossCallback host_gpu_loss_callback, VulkanProvider& provider) {
|
||||
auto presenter = std::unique_ptr<VulkanPresenter>(
|
||||
new VulkanPresenter(host_gpu_loss_callback, provider));
|
||||
HostGpuLossCallback host_gpu_loss_callback,
|
||||
const VulkanDevice* vulkan_device, const UISamplers* ui_samplers) {
|
||||
auto presenter = std::unique_ptr<VulkanPresenter>(new VulkanPresenter(
|
||||
host_gpu_loss_callback, vulkan_device, ui_samplers));
|
||||
if (!presenter->InitializeSurfaceIndependent()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -134,29 +136,10 @@ class VulkanPresenter final : public Presenter {
|
|||
|
||||
~VulkanPresenter();
|
||||
|
||||
VulkanProvider& provider() const { return provider_; }
|
||||
const VulkanDevice* vulkan_device() const { return vulkan_device_; }
|
||||
|
||||
static Surface::TypeFlags GetSurfaceTypesSupportedByInstance(
|
||||
const VulkanProvider::InstanceExtensions& instance_extensions) {
|
||||
if (!instance_extensions.khr_surface) {
|
||||
return 0;
|
||||
}
|
||||
Surface::TypeFlags type_flags = 0;
|
||||
#if XE_PLATFORM_ANDROID
|
||||
if (instance_extensions.khr_android_surface) {
|
||||
type_flags |= Surface::kTypeFlag_AndroidNativeWindow;
|
||||
}
|
||||
#elif XE_PLATFORM_GNU_LINUX
|
||||
if (instance_extensions.khr_xcb_surface) {
|
||||
type_flags |= Surface::kTypeFlag_XcbWindow;
|
||||
}
|
||||
#elif XE_PLATFORM_WIN32
|
||||
if (instance_extensions.khr_win32_surface) {
|
||||
type_flags |= Surface::kTypeFlag_Win32Hwnd;
|
||||
}
|
||||
#endif
|
||||
return type_flags;
|
||||
}
|
||||
const VulkanInstance::Extensions& instance_extensions);
|
||||
Surface::TypeFlags GetSupportedSurfaceTypes() const override;
|
||||
|
||||
bool CaptureGuestOutput(RawImage& image_out) override;
|
||||
|
|
@ -186,11 +169,12 @@ class VulkanPresenter final : public Presenter {
|
|||
class GuestOutputImage {
|
||||
public:
|
||||
static std::unique_ptr<GuestOutputImage> Create(
|
||||
const VulkanProvider& provider, uint32_t width, uint32_t height) {
|
||||
const VulkanDevice* const vulkan_device, const uint32_t width,
|
||||
const uint32_t height) {
|
||||
assert_not_zero(width);
|
||||
assert_not_zero(height);
|
||||
auto image = std::unique_ptr<GuestOutputImage>(
|
||||
new GuestOutputImage(provider, width, height));
|
||||
new GuestOutputImage(vulkan_device, width, height));
|
||||
if (!image->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -208,16 +192,16 @@ class VulkanPresenter final : public Presenter {
|
|||
VkImageView view() const { return view_; }
|
||||
|
||||
private:
|
||||
GuestOutputImage(const VulkanProvider& provider, uint32_t width,
|
||||
uint32_t height)
|
||||
: provider_(provider) {
|
||||
GuestOutputImage(const VulkanDevice* const vulkan_device,
|
||||
const uint32_t width, const uint32_t height)
|
||||
: vulkan_device_(vulkan_device) {
|
||||
extent_.width = width;
|
||||
extent_.height = height;
|
||||
}
|
||||
|
||||
bool Initialize();
|
||||
|
||||
const VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
|
||||
VkExtent2D extent_;
|
||||
VkImage image_ = VK_NULL_HANDLE;
|
||||
|
|
@ -299,8 +283,9 @@ class VulkanPresenter final : public Presenter {
|
|||
class Submission {
|
||||
public:
|
||||
static std::unique_ptr<Submission> Create(
|
||||
const VulkanProvider& provider) {
|
||||
auto submission = std::unique_ptr<Submission>(new Submission(provider));
|
||||
const VulkanDevice* const vulkan_device) {
|
||||
auto submission =
|
||||
std::unique_ptr<Submission>(new Submission(vulkan_device));
|
||||
if (!submission->Initialize()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -319,11 +304,11 @@ class VulkanPresenter final : public Presenter {
|
|||
}
|
||||
|
||||
private:
|
||||
explicit Submission(const VulkanProvider& provider)
|
||||
: provider_(provider) {}
|
||||
explicit Submission(const VulkanDevice* const vulkan_device)
|
||||
: vulkan_device_(vulkan_device) {}
|
||||
bool Initialize();
|
||||
|
||||
const VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
VkSemaphore acquire_semaphore_ = VK_NULL_HANDLE;
|
||||
VkSemaphore present_semaphore_ = VK_NULL_HANDLE;
|
||||
VkCommandPool draw_command_pool_ = VK_NULL_HANDLE;
|
||||
|
|
@ -376,8 +361,8 @@ class VulkanPresenter final : public Presenter {
|
|||
VkFramebuffer framebuffer;
|
||||
};
|
||||
|
||||
explicit PaintContext(VulkanProvider& provider)
|
||||
: provider(provider), submission_tracker(provider) {}
|
||||
explicit PaintContext(const VulkanDevice* const vulkan_device)
|
||||
: vulkan_device(vulkan_device), submission_tracker(vulkan_device) {}
|
||||
PaintContext(const PaintContext& paint_context) = delete;
|
||||
PaintContext& operator=(const PaintContext& paint_context) = delete;
|
||||
|
||||
|
|
@ -386,7 +371,7 @@ class VulkanPresenter final : public Presenter {
|
|||
// technically retire it, so it will be in an undefined state), and needs to
|
||||
// be destroyed externally no matter what the result is.
|
||||
static VkSwapchainKHR CreateSwapchainForVulkanSurface(
|
||||
const VulkanProvider& provider, VkSurfaceKHR surface, uint32_t width,
|
||||
const VulkanDevice* vulkan_device, VkSurfaceKHR surface, uint32_t width,
|
||||
uint32_t height, VkSwapchainKHR old_swapchain,
|
||||
uint32_t& present_queue_family_out, VkFormat& image_format_out,
|
||||
VkExtent2D& image_extent_out, bool& is_fifo_out,
|
||||
|
|
@ -401,7 +386,7 @@ class VulkanPresenter final : public Presenter {
|
|||
|
||||
// Connection-indepedent.
|
||||
|
||||
const VulkanProvider& provider;
|
||||
const VulkanDevice* vulkan_device;
|
||||
|
||||
std::array<std::unique_ptr<PaintContext::Submission>, kSubmissionCount>
|
||||
submissions;
|
||||
|
|
@ -460,19 +445,25 @@ class VulkanPresenter final : public Presenter {
|
|||
};
|
||||
|
||||
explicit VulkanPresenter(HostGpuLossCallback host_gpu_loss_callback,
|
||||
VulkanProvider& provider)
|
||||
const VulkanDevice* vulkan_device,
|
||||
const UISamplers* ui_samplers)
|
||||
: Presenter(host_gpu_loss_callback),
|
||||
provider_(provider),
|
||||
guest_output_image_refresher_submission_tracker_(provider),
|
||||
ui_submission_tracker_(provider),
|
||||
paint_context_(provider) {}
|
||||
vulkan_device_(vulkan_device),
|
||||
ui_samplers_(ui_samplers),
|
||||
guest_output_image_refresher_submission_tracker_(vulkan_device),
|
||||
ui_submission_tracker_(vulkan_device),
|
||||
paint_context_(vulkan_device) {
|
||||
assert_not_null(vulkan_device);
|
||||
assert_not_null(ui_samplers);
|
||||
}
|
||||
|
||||
bool InitializeSurfaceIndependent();
|
||||
|
||||
[[nodiscard]] VkPipeline CreateGuestOutputPaintPipeline(
|
||||
GuestOutputPaintEffect effect, VkRenderPass render_pass);
|
||||
|
||||
VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
const UISamplers* ui_samplers_;
|
||||
|
||||
// Static objects for guest output presentation, used only when painting the
|
||||
// main target (can be destroyed only after awaiting main target usage
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2025 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
|
@ -10,46 +10,12 @@
|
|||
#ifndef XENIA_UI_VULKAN_VULKAN_PROVIDER_H_
|
||||
#define XENIA_UI_VULKAN_VULKAN_PROVIDER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/ui/graphics_provider.h"
|
||||
#include "xenia/ui/renderdoc_api.h"
|
||||
|
||||
#if XE_PLATFORM_ANDROID
|
||||
#ifndef VK_USE_PLATFORM_ANDROID_KHR
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR 1
|
||||
#endif
|
||||
#elif XE_PLATFORM_GNU_LINUX
|
||||
#ifndef VK_USE_PLATFORM_XCB_KHR
|
||||
#define VK_USE_PLATFORM_XCB_KHR 1
|
||||
#endif
|
||||
#elif XE_PLATFORM_WIN32
|
||||
// Must be included before vulkan.h with VK_USE_PLATFORM_WIN32_KHR because it
|
||||
// includes Windows.h too.
|
||||
#include "xenia/base/platform_win.h"
|
||||
#ifndef VK_USE_PLATFORM_WIN32_KHR
|
||||
#define VK_USE_PLATFORM_WIN32_KHR 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef VK_ENABLE_BETA_EXTENSIONS
|
||||
#define VK_ENABLE_BETA_EXTENSIONS 1
|
||||
#endif
|
||||
#ifndef VK_NO_PROTOTYPES
|
||||
#define VK_NO_PROTOTYPES 1
|
||||
#endif
|
||||
#include "third_party/Vulkan-Headers/include/vulkan/vulkan.h"
|
||||
|
||||
#define XELOGVK XELOGI
|
||||
|
||||
#define XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES 1
|
||||
#include "xenia/ui/vulkan/ui_samplers.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
#include "xenia/ui/vulkan/vulkan_instance.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
|
@ -57,163 +23,15 @@ namespace vulkan {
|
|||
|
||||
class VulkanProvider : public GraphicsProvider {
|
||||
public:
|
||||
struct DeviceInfo {
|
||||
// "ext_1_X"-prefixed extension fields are set to true not only if the
|
||||
// extension itself is actually exposed, but also if it was promoted to the
|
||||
// device's API version. Therefore, merely the field being set to true
|
||||
// doesn't imply that all the required features in the extension are
|
||||
// supported - actual properties and features must be checked rather than
|
||||
// the extension itself where they matter.
|
||||
static std::unique_ptr<VulkanProvider> Create(bool with_gpu_emulation,
|
||||
bool with_presentation);
|
||||
|
||||
// Vulkan 1.0.
|
||||
VulkanInstance* vulkan_instance() const { return vulkan_instance_.get(); }
|
||||
|
||||
uint32_t memory_types_device_local;
|
||||
uint32_t memory_types_host_visible;
|
||||
uint32_t memory_types_host_coherent;
|
||||
uint32_t memory_types_host_cached;
|
||||
VulkanDevice* vulkan_device() const { return vulkan_device_.get(); }
|
||||
|
||||
uint32_t apiVersion;
|
||||
uint32_t maxImageDimension2D;
|
||||
uint32_t maxImageDimension3D;
|
||||
uint32_t maxImageDimensionCube;
|
||||
uint32_t maxImageArrayLayers;
|
||||
uint32_t maxStorageBufferRange;
|
||||
uint32_t maxSamplerAllocationCount;
|
||||
uint32_t maxPerStageDescriptorSamplers;
|
||||
uint32_t maxPerStageDescriptorStorageBuffers;
|
||||
uint32_t maxPerStageDescriptorSampledImages;
|
||||
uint32_t maxPerStageResources;
|
||||
uint32_t maxVertexOutputComponents;
|
||||
uint32_t maxTessellationEvaluationOutputComponents;
|
||||
uint32_t maxGeometryInputComponents;
|
||||
uint32_t maxGeometryOutputComponents;
|
||||
uint32_t maxGeometryTotalOutputComponents;
|
||||
uint32_t maxFragmentInputComponents;
|
||||
uint32_t maxFragmentCombinedOutputResources;
|
||||
float maxSamplerAnisotropy;
|
||||
uint32_t maxViewportDimensions[2];
|
||||
float viewportBoundsRange[2];
|
||||
VkDeviceSize minUniformBufferOffsetAlignment;
|
||||
VkDeviceSize minStorageBufferOffsetAlignment;
|
||||
uint32_t maxFramebufferWidth;
|
||||
uint32_t maxFramebufferHeight;
|
||||
VkSampleCountFlags framebufferColorSampleCounts;
|
||||
VkSampleCountFlags framebufferDepthSampleCounts;
|
||||
VkSampleCountFlags framebufferStencilSampleCounts;
|
||||
VkSampleCountFlags framebufferNoAttachmentsSampleCounts;
|
||||
VkSampleCountFlags sampledImageColorSampleCounts;
|
||||
VkSampleCountFlags sampledImageIntegerSampleCounts;
|
||||
VkSampleCountFlags sampledImageDepthSampleCounts;
|
||||
VkSampleCountFlags sampledImageStencilSampleCounts;
|
||||
VkSampleCountFlags standardSampleLocations;
|
||||
VkDeviceSize optimalBufferCopyOffsetAlignment;
|
||||
VkDeviceSize optimalBufferCopyRowPitchAlignment;
|
||||
VkDeviceSize nonCoherentAtomSize;
|
||||
|
||||
bool fullDrawIndexUint32;
|
||||
bool independentBlend;
|
||||
bool geometryShader;
|
||||
bool tessellationShader;
|
||||
bool sampleRateShading;
|
||||
bool depthClamp;
|
||||
bool fillModeNonSolid;
|
||||
bool samplerAnisotropy;
|
||||
bool vertexPipelineStoresAndAtomics;
|
||||
bool fragmentStoresAndAtomics;
|
||||
bool shaderClipDistance;
|
||||
bool shaderCullDistance;
|
||||
bool sparseBinding;
|
||||
bool sparseResidencyBuffer;
|
||||
|
||||
// VK_KHR_swapchain (#2).
|
||||
|
||||
bool ext_VK_KHR_swapchain;
|
||||
|
||||
// VK_KHR_sampler_mirror_clamp_to_edge (#15, Vulkan 1.2).
|
||||
|
||||
bool ext_1_2_VK_KHR_sampler_mirror_clamp_to_edge;
|
||||
|
||||
bool samplerMirrorClampToEdge;
|
||||
|
||||
// VK_KHR_dedicated_allocation (#128, Vulkan 1.1).
|
||||
|
||||
bool ext_1_1_VK_KHR_dedicated_allocation;
|
||||
|
||||
// VK_EXT_shader_stencil_export (#141).
|
||||
|
||||
bool ext_VK_EXT_shader_stencil_export;
|
||||
|
||||
// VK_KHR_get_memory_requirements2 (#147, Vulkan 1.1).
|
||||
|
||||
bool ext_1_1_VK_KHR_get_memory_requirements2;
|
||||
|
||||
// VK_KHR_image_format_list (#148, Vulkan 1.2).
|
||||
|
||||
bool ext_1_2_VK_KHR_image_format_list;
|
||||
|
||||
// VK_KHR_sampler_ycbcr_conversion (#157, Vulkan 1.1).
|
||||
|
||||
bool ext_1_1_VK_KHR_sampler_ycbcr_conversion;
|
||||
|
||||
// VK_KHR_bind_memory2 (#158, Vulkan 1.1).
|
||||
|
||||
bool ext_1_1_VK_KHR_bind_memory2;
|
||||
|
||||
// VK_KHR_portability_subset (#164).
|
||||
|
||||
bool ext_VK_KHR_portability_subset;
|
||||
|
||||
bool constantAlphaColorBlendFactors;
|
||||
bool imageViewFormatReinterpretation;
|
||||
bool imageViewFormatSwizzle;
|
||||
bool pointPolygons;
|
||||
bool separateStencilMaskRef;
|
||||
bool shaderSampleRateInterpolationFunctions;
|
||||
bool triangleFans;
|
||||
|
||||
// VK_KHR_shader_float_controls (#198, Vulkan 1.2).
|
||||
|
||||
bool ext_1_2_VK_KHR_shader_float_controls;
|
||||
|
||||
bool shaderSignedZeroInfNanPreserveFloat32;
|
||||
bool shaderDenormFlushToZeroFloat32;
|
||||
bool shaderRoundingModeRTEFloat32;
|
||||
|
||||
// VK_KHR_spirv_1_4 (#237, Vulkan 1.2).
|
||||
|
||||
bool ext_1_2_VK_KHR_spirv_1_4;
|
||||
|
||||
// VK_EXT_memory_budget (#238).
|
||||
|
||||
bool ext_VK_EXT_memory_budget;
|
||||
|
||||
// VK_EXT_fragment_shader_interlock (#252).
|
||||
|
||||
bool ext_VK_EXT_fragment_shader_interlock;
|
||||
|
||||
bool fragmentShaderSampleInterlock;
|
||||
bool fragmentShaderPixelInterlock;
|
||||
|
||||
// VK_EXT_shader_demote_to_helper_invocation (#277, Vulkan 1.3).
|
||||
|
||||
bool ext_1_3_VK_EXT_shader_demote_to_helper_invocation;
|
||||
|
||||
bool shaderDemoteToHelperInvocation;
|
||||
|
||||
// VK_KHR_maintenance4 (#414, Vulkan 1.3).
|
||||
|
||||
bool ext_1_3_VK_KHR_maintenance4;
|
||||
|
||||
// VK_EXT_non_seamless_cube_map (#423).
|
||||
|
||||
bool ext_VK_EXT_non_seamless_cube_map;
|
||||
|
||||
bool nonSeamlessCubeMap;
|
||||
};
|
||||
|
||||
~VulkanProvider();
|
||||
|
||||
static std::unique_ptr<VulkanProvider> Create(bool is_surface_required);
|
||||
// nullptr if created without presentation support.
|
||||
const UISamplers* ui_samplers() const { return ui_samplers_.get(); }
|
||||
|
||||
std::unique_ptr<Presenter> CreatePresenter(
|
||||
Presenter::HostGpuLossCallback host_gpu_loss_callback =
|
||||
|
|
@ -221,209 +39,16 @@ class VulkanProvider : public GraphicsProvider {
|
|||
|
||||
std::unique_ptr<ImmediateDrawer> CreateImmediateDrawer() override;
|
||||
|
||||
const RenderdocApi& renderdoc_api() const { return renderdoc_api_; }
|
||||
|
||||
struct LibraryFunctions {
|
||||
// From the module.
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
||||
PFN_vkDestroyInstance vkDestroyInstance;
|
||||
// From vkGetInstanceProcAddr.
|
||||
PFN_vkCreateInstance vkCreateInstance;
|
||||
PFN_vkEnumerateInstanceExtensionProperties
|
||||
vkEnumerateInstanceExtensionProperties;
|
||||
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
|
||||
struct {
|
||||
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion;
|
||||
} v_1_1;
|
||||
};
|
||||
const LibraryFunctions& lfn() const { return lfn_; }
|
||||
|
||||
struct InstanceExtensions {
|
||||
bool ext_debug_utils;
|
||||
// Core since 1.1.0.
|
||||
bool khr_get_physical_device_properties2;
|
||||
|
||||
// Surface extensions.
|
||||
bool khr_surface;
|
||||
#if XE_PLATFORM_ANDROID
|
||||
bool khr_android_surface;
|
||||
#elif XE_PLATFORM_GNU_LINUX
|
||||
bool khr_xcb_surface;
|
||||
#elif XE_PLATFORM_WIN32
|
||||
bool khr_win32_surface;
|
||||
#endif
|
||||
};
|
||||
const InstanceExtensions& instance_extensions() const {
|
||||
return instance_extensions_;
|
||||
}
|
||||
VkInstance instance() const { return instance_; }
|
||||
struct InstanceFunctions {
|
||||
#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name;
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
PFN_##core_name core_name;
|
||||
#include "xenia/ui/vulkan/functions/instance_1_0.inc"
|
||||
#include "xenia/ui/vulkan/functions/instance_ext_debug_utils.inc"
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_get_physical_device_properties2.inc"
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_surface.inc"
|
||||
#if XE_PLATFORM_ANDROID
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_android_surface.inc"
|
||||
#elif XE_PLATFORM_GNU_LINUX
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_xcb_surface.inc"
|
||||
#elif XE_PLATFORM_WIN32
|
||||
#include "xenia/ui/vulkan/functions/instance_khr_win32_surface.inc"
|
||||
#endif
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
#undef XE_UI_VULKAN_FUNCTION
|
||||
};
|
||||
const InstanceFunctions& ifn() const { return ifn_; }
|
||||
|
||||
VkPhysicalDevice physical_device() const { return physical_device_; }
|
||||
|
||||
const DeviceInfo& device_info() const { return device_info_; }
|
||||
|
||||
struct QueueFamily {
|
||||
uint32_t queue_first_index = 0;
|
||||
uint32_t queue_count = 0;
|
||||
bool potentially_supports_present = false;
|
||||
};
|
||||
const std::vector<QueueFamily>& queue_families() const {
|
||||
return queue_families_;
|
||||
}
|
||||
// Required.
|
||||
uint32_t queue_family_graphics_compute() const {
|
||||
return queue_family_graphics_compute_;
|
||||
}
|
||||
// Optional, if sparse binding is supported (UINT32_MAX otherwise). May be the
|
||||
// same as queue_family_graphics_compute_.
|
||||
uint32_t queue_family_sparse_binding() const {
|
||||
return queue_family_sparse_binding_;
|
||||
}
|
||||
|
||||
struct Queue {
|
||||
VkQueue queue = VK_NULL_HANDLE;
|
||||
std::recursive_mutex mutex;
|
||||
};
|
||||
struct QueueAcquisition {
|
||||
QueueAcquisition(std::unique_lock<std::recursive_mutex>&& lock,
|
||||
VkQueue queue)
|
||||
: lock(std::move(lock)), queue(queue) {}
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
VkQueue queue;
|
||||
};
|
||||
QueueAcquisition AcquireQueue(uint32_t index) {
|
||||
Queue& queue = queues_[index];
|
||||
return QueueAcquisition(std::unique_lock<std::recursive_mutex>(queue.mutex),
|
||||
queue.queue);
|
||||
}
|
||||
QueueAcquisition AcquireQueue(uint32_t family_index, uint32_t index) {
|
||||
assert_true(family_index != UINT32_MAX);
|
||||
return AcquireQueue(queue_families_[family_index].queue_first_index +
|
||||
index);
|
||||
}
|
||||
|
||||
VkDevice device() const { return device_; }
|
||||
struct DeviceFunctions {
|
||||
#define XE_UI_VULKAN_FUNCTION(name) PFN_##name name;
|
||||
#define XE_UI_VULKAN_FUNCTION_PROMOTED(extension_name, core_name) \
|
||||
PFN_##core_name core_name;
|
||||
#include "xenia/ui/vulkan/functions/device_1_0.inc"
|
||||
#include "xenia/ui/vulkan/functions/device_khr_bind_memory2.inc"
|
||||
#include "xenia/ui/vulkan/functions/device_khr_get_memory_requirements2.inc"
|
||||
#include "xenia/ui/vulkan/functions/device_khr_maintenance4.inc"
|
||||
#include "xenia/ui/vulkan/functions/device_khr_swapchain.inc"
|
||||
#undef XE_UI_VULKAN_FUNCTION_PROMOTED
|
||||
#undef XE_UI_VULKAN_FUNCTION
|
||||
};
|
||||
const DeviceFunctions& dfn() const { return dfn_; }
|
||||
|
||||
template <typename T>
|
||||
void SetDeviceObjectName(VkObjectType type, T handle,
|
||||
const char* name) const {
|
||||
if (!debug_names_used_) {
|
||||
return;
|
||||
}
|
||||
VkDebugUtilsObjectNameInfoEXT name_info;
|
||||
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
name_info.pNext = nullptr;
|
||||
name_info.objectType = type;
|
||||
name_info.objectHandle = uint64_t(handle);
|
||||
name_info.pObjectName = name;
|
||||
ifn_.vkSetDebugUtilsObjectNameEXT(device_, &name_info);
|
||||
}
|
||||
|
||||
// Samplers that may be useful for host needs. Only these samplers should be
|
||||
// used in host, non-emulation contexts, because the total number of samplers
|
||||
// is heavily limited (4000) on Nvidia GPUs - the rest of samplers are
|
||||
// allocated for emulation.
|
||||
enum class HostSampler {
|
||||
kNearestClamp,
|
||||
kLinearClamp,
|
||||
kNearestRepeat,
|
||||
kLinearRepeat,
|
||||
|
||||
kCount,
|
||||
};
|
||||
VkSampler GetHostSampler(HostSampler sampler) const {
|
||||
return host_samplers_[size_t(sampler)];
|
||||
}
|
||||
|
||||
private:
|
||||
explicit VulkanProvider(bool is_surface_required)
|
||||
: is_surface_required_(is_surface_required) {}
|
||||
explicit VulkanProvider() = default;
|
||||
|
||||
bool Initialize();
|
||||
std::unique_ptr<VulkanInstance> vulkan_instance_;
|
||||
|
||||
static void AccumulateInstanceExtensions(
|
||||
size_t properties_count, const VkExtensionProperties* properties,
|
||||
bool request_debug_utils, InstanceExtensions& instance_extensions,
|
||||
std::vector<const char*>& instance_extensions_enabled);
|
||||
// Depends on the instance.
|
||||
std::unique_ptr<VulkanDevice> vulkan_device_;
|
||||
|
||||
static VkBool32 VKAPI_CALL DebugMessengerCallback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT message_types,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callback_data,
|
||||
void* user_data);
|
||||
|
||||
// For the current `physical_device_`, sets up the members obtained from the
|
||||
// physical device info, and tries to create a device and get the needed
|
||||
// queues.
|
||||
// The call is successful if `device_` is not VK_NULL_HANDLE as a result.
|
||||
void TryCreateDevice();
|
||||
|
||||
bool is_surface_required_;
|
||||
|
||||
RenderdocApi renderdoc_api_;
|
||||
|
||||
#if XE_PLATFORM_LINUX
|
||||
void* library_ = nullptr;
|
||||
#elif XE_PLATFORM_WIN32
|
||||
HMODULE library_ = nullptr;
|
||||
#endif
|
||||
|
||||
LibraryFunctions lfn_ = {};
|
||||
|
||||
InstanceExtensions instance_extensions_;
|
||||
VkInstance instance_ = VK_NULL_HANDLE;
|
||||
InstanceFunctions ifn_;
|
||||
|
||||
VkDebugUtilsMessengerEXT debug_messenger_ = VK_NULL_HANDLE;
|
||||
bool debug_names_used_ = false;
|
||||
|
||||
VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
|
||||
|
||||
DeviceInfo device_info_ = {};
|
||||
|
||||
std::vector<QueueFamily> queue_families_;
|
||||
uint32_t queue_family_graphics_compute_;
|
||||
uint32_t queue_family_sparse_binding_;
|
||||
|
||||
VkDevice device_ = VK_NULL_HANDLE;
|
||||
DeviceFunctions dfn_ = {};
|
||||
|
||||
// Queues contain a mutex, can't use std::vector.
|
||||
std::unique_ptr<Queue[]> queues_;
|
||||
|
||||
VkSampler host_samplers_[size_t(HostSampler::kCount)] = {};
|
||||
// Depends on the device.
|
||||
std::unique_ptr<UISamplers> ui_samplers_;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ VulkanSubmissionTracker::FenceAcquisition::~FenceAcquisition() {
|
|||
|
||||
void VulkanSubmissionTracker::Shutdown() {
|
||||
AwaitAllSubmissionsCompletion();
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
for (VkFence fence : fences_reclaimed_) {
|
||||
dfn.vkDestroyFence(device, fence, nullptr);
|
||||
}
|
||||
|
|
@ -71,8 +71,8 @@ void VulkanSubmissionTracker::FenceAcquisition::SubmissionFailedOrDropped() {
|
|||
|
||||
uint64_t VulkanSubmissionTracker::UpdateAndGetCompletedSubmission() {
|
||||
if (!fences_pending_.empty()) {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
while (!fences_pending_.empty()) {
|
||||
const std::pair<uint64_t, VkFence>& pending_pair =
|
||||
fences_pending_.front();
|
||||
|
|
@ -113,8 +113,8 @@ bool VulkanSubmissionTracker::AwaitSubmissionCompletion(
|
|||
// in submission order."
|
||||
size_t reclaim_end = fences_pending_.size();
|
||||
if (reclaim_end) {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
while (reclaim_end) {
|
||||
const std::pair<uint64_t, VkFence>& pending_pair =
|
||||
fences_pending_[reclaim_end - 1];
|
||||
|
|
@ -149,8 +149,8 @@ VulkanSubmissionTracker::AcquireFenceToAdvanceSubmission() {
|
|||
// Reclaim fences if the client only gets the completed submission index or
|
||||
// awaits in special cases such as shutdown.
|
||||
UpdateAndGetCompletedSubmission();
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
if (!fences_reclaimed_.empty()) {
|
||||
VkFence reclaimed_fence = fences_reclaimed_.back();
|
||||
if (dfn.vkResetFences(device, 1, &reclaimed_fence) == VK_SUCCESS) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
|
@ -87,11 +88,16 @@ class VulkanSubmissionTracker {
|
|||
bool signal_failed_ = false;
|
||||
};
|
||||
|
||||
VulkanSubmissionTracker(VulkanProvider& provider) : provider_(provider) {}
|
||||
VulkanSubmissionTracker(const VulkanDevice* vulkan_device)
|
||||
: vulkan_device_(vulkan_device) {
|
||||
assert_not_null(vulkan_device);
|
||||
}
|
||||
|
||||
VulkanSubmissionTracker(const VulkanSubmissionTracker& submission_tracker) =
|
||||
delete;
|
||||
VulkanSubmissionTracker& operator=(
|
||||
const VulkanSubmissionTracker& submission_tracker) = delete;
|
||||
|
||||
~VulkanSubmissionTracker() { Shutdown(); }
|
||||
|
||||
void Shutdown();
|
||||
|
|
@ -112,7 +118,7 @@ class VulkanSubmissionTracker {
|
|||
[[nodiscard]] FenceAcquisition AcquireFenceToAdvanceSubmission();
|
||||
|
||||
private:
|
||||
VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
uint64_t submission_current_ = 1;
|
||||
// Last submission with a successful fence signal as well as a successful
|
||||
// fence wait / query.
|
||||
|
|
|
|||
|
|
@ -24,12 +24,13 @@ namespace vulkan {
|
|||
// or flush range must be clamped to the actual allocation size as a special
|
||||
// case, but it's still unlikely that the allocation won't be aligned to it), so
|
||||
// try not to waste that padding.
|
||||
VulkanUploadBufferPool::VulkanUploadBufferPool(const VulkanProvider& provider,
|
||||
VkBufferUsageFlags usage,
|
||||
size_t page_size)
|
||||
VulkanUploadBufferPool::VulkanUploadBufferPool(
|
||||
const VulkanDevice* const vulkan_device, const VkBufferUsageFlags usage,
|
||||
const size_t page_size)
|
||||
: GraphicsUploadBufferPool(size_t(
|
||||
util::GetMappableMemorySize(provider, VkDeviceSize(page_size)))),
|
||||
provider_(provider),
|
||||
xe::round_up(VkDeviceSize(page_size),
|
||||
vulkan_device->properties().nonCoherentAtomSize))),
|
||||
vulkan_device_(vulkan_device),
|
||||
usage_(usage) {}
|
||||
|
||||
uint8_t* VulkanUploadBufferPool::Request(uint64_t submission_index, size_t size,
|
||||
|
|
@ -72,8 +73,8 @@ VulkanUploadBufferPool::CreatePageImplementation() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
|
||||
VkBufferCreateInfo buffer_create_info;
|
||||
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
|
|
@ -94,8 +95,9 @@ VulkanUploadBufferPool::CreatePageImplementation() {
|
|||
if (memory_type_ == kMemoryTypeUnknown) {
|
||||
VkMemoryRequirements memory_requirements;
|
||||
dfn.vkGetBufferMemoryRequirements(device, buffer, &memory_requirements);
|
||||
memory_type_ = util::ChooseHostMemoryType(
|
||||
provider_, memory_requirements.memoryTypeBits, false);
|
||||
memory_type_ =
|
||||
util::ChooseHostMemoryType(vulkan_device_->memory_types(),
|
||||
memory_requirements.memoryTypeBits, false);
|
||||
if (memory_type_ == UINT32_MAX) {
|
||||
XELOGE(
|
||||
"No host-visible memory types can store an Vulkan upload buffer with "
|
||||
|
|
@ -117,7 +119,8 @@ VulkanUploadBufferPool::CreatePageImplementation() {
|
|||
dfn.vkGetBufferMemoryRequirements(device, buffer_expanded,
|
||||
&memory_requirements_expanded);
|
||||
uint32_t memory_type_expanded = util::ChooseHostMemoryType(
|
||||
provider_, memory_requirements.memoryTypeBits, false);
|
||||
vulkan_device_->memory_types(), memory_requirements.memoryTypeBits,
|
||||
false);
|
||||
if (memory_requirements_expanded.size <= allocation_size_ &&
|
||||
memory_type_expanded != UINT32_MAX) {
|
||||
page_size_ = size_t(allocation_size_);
|
||||
|
|
@ -139,7 +142,7 @@ VulkanUploadBufferPool::CreatePageImplementation() {
|
|||
memory_allocate_info.allocationSize = allocation_size_;
|
||||
memory_allocate_info.memoryTypeIndex = memory_type_;
|
||||
VkMemoryDedicatedAllocateInfo memory_dedicated_allocate_info;
|
||||
if (provider_.device_info().ext_1_1_VK_KHR_dedicated_allocation) {
|
||||
if (vulkan_device_->extensions().ext_1_1_KHR_dedicated_allocation) {
|
||||
memory_allocate_info_last->pNext = &memory_dedicated_allocate_info;
|
||||
memory_allocate_info_last = reinterpret_cast<VkMemoryAllocateInfo*>(
|
||||
&memory_dedicated_allocate_info);
|
||||
|
|
@ -176,19 +179,19 @@ VulkanUploadBufferPool::CreatePageImplementation() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return new VulkanPage(provider_, buffer, memory, mapping);
|
||||
return new VulkanPage(vulkan_device_, buffer, memory, mapping);
|
||||
}
|
||||
|
||||
void VulkanUploadBufferPool::FlushPageWrites(Page* page, size_t offset,
|
||||
size_t size) {
|
||||
util::FlushMappedMemoryRange(
|
||||
provider_, static_cast<const VulkanPage*>(page)->memory_, memory_type_,
|
||||
VkDeviceSize(offset), allocation_size_, VkDeviceSize(size));
|
||||
vulkan_device_, static_cast<const VulkanPage*>(page)->memory_,
|
||||
memory_type_, VkDeviceSize(offset), allocation_size_, VkDeviceSize(size));
|
||||
}
|
||||
|
||||
VulkanUploadBufferPool::VulkanPage::~VulkanPage() {
|
||||
const VulkanProvider::DeviceFunctions& dfn = provider_.dfn();
|
||||
VkDevice device = provider_.device();
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
dfn.vkDestroyBuffer(device, buffer_, nullptr);
|
||||
// Unmapping is done implicitly when the memory is freed.
|
||||
dfn.vkFreeMemory(device, memory_, nullptr);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#define XENIA_UI_VULKAN_VULKAN_UPLOAD_BUFFER_POOL_H_
|
||||
|
||||
#include "xenia/ui/graphics_upload_buffer_pool.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
|
@ -19,7 +19,7 @@ namespace vulkan {
|
|||
|
||||
class VulkanUploadBufferPool : public GraphicsUploadBufferPool {
|
||||
public:
|
||||
VulkanUploadBufferPool(const VulkanProvider& provider,
|
||||
VulkanUploadBufferPool(const VulkanDevice* vulkan_device,
|
||||
VkBufferUsageFlags usage,
|
||||
size_t page_size = kDefaultPageSize);
|
||||
|
||||
|
|
@ -37,20 +37,20 @@ class VulkanUploadBufferPool : public GraphicsUploadBufferPool {
|
|||
private:
|
||||
struct VulkanPage : public Page {
|
||||
// Takes ownership of the buffer and its memory and mapping.
|
||||
VulkanPage(const VulkanProvider& provider, VkBuffer buffer,
|
||||
VulkanPage(const VulkanDevice* vulkan_device, VkBuffer buffer,
|
||||
VkDeviceMemory memory, void* mapping)
|
||||
: provider_(provider),
|
||||
: vulkan_device_(vulkan_device),
|
||||
buffer_(buffer),
|
||||
memory_(memory),
|
||||
mapping_(mapping) {}
|
||||
~VulkanPage() override;
|
||||
const VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
VkBuffer buffer_;
|
||||
VkDeviceMemory memory_;
|
||||
void* mapping_;
|
||||
};
|
||||
|
||||
const VulkanProvider& provider_;
|
||||
const VulkanDevice* vulkan_device_;
|
||||
|
||||
VkDeviceSize allocation_size_;
|
||||
static constexpr uint32_t kMemoryTypeUnknown = UINT32_MAX;
|
||||
|
|
|
|||
|
|
@ -13,21 +13,23 @@
|
|||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
namespace util {
|
||||
|
||||
void FlushMappedMemoryRange(const VulkanProvider& provider,
|
||||
VkDeviceMemory memory, uint32_t memory_type,
|
||||
VkDeviceSize offset, VkDeviceSize memory_size,
|
||||
VkDeviceSize size) {
|
||||
void FlushMappedMemoryRange(const VulkanDevice* const vulkan_device,
|
||||
const VkDeviceMemory memory,
|
||||
const uint32_t memory_type,
|
||||
const VkDeviceSize offset,
|
||||
const VkDeviceSize memory_size,
|
||||
const VkDeviceSize size) {
|
||||
assert_false(size != VK_WHOLE_SIZE && memory_size == VK_WHOLE_SIZE);
|
||||
assert_true(memory_size == VK_WHOLE_SIZE || offset <= memory_size);
|
||||
assert_true(memory_size == VK_WHOLE_SIZE || size <= memory_size - offset);
|
||||
if (!size || (provider.device_info().memory_types_host_coherent &
|
||||
if (!size || (vulkan_device->memory_types().host_coherent &
|
||||
(uint32_t(1) << memory_type))) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -37,27 +39,25 @@ void FlushMappedMemoryRange(const VulkanProvider& provider,
|
|||
range.memory = memory;
|
||||
range.offset = offset;
|
||||
range.size = size;
|
||||
VkDeviceSize non_coherent_atom_size =
|
||||
provider.device_info().nonCoherentAtomSize;
|
||||
// On some Android implementations, nonCoherentAtomSize is 0, not 1.
|
||||
if (non_coherent_atom_size > 1) {
|
||||
const VkDeviceSize non_coherent_atom_size =
|
||||
vulkan_device->properties().nonCoherentAtomSize;
|
||||
range.offset = offset / non_coherent_atom_size * non_coherent_atom_size;
|
||||
if (size != VK_WHOLE_SIZE) {
|
||||
range.size = std::min(xe::round_up(offset + size, non_coherent_atom_size),
|
||||
memory_size) -
|
||||
range.offset;
|
||||
}
|
||||
}
|
||||
provider.dfn().vkFlushMappedMemoryRanges(provider.device(), 1, &range);
|
||||
vulkan_device->functions().vkFlushMappedMemoryRanges(vulkan_device->device(),
|
||||
1, &range);
|
||||
}
|
||||
|
||||
bool CreateDedicatedAllocationBuffer(
|
||||
const VulkanProvider& provider, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
MemoryPurpose memory_purpose, VkBuffer& buffer_out,
|
||||
VkDeviceMemory& memory_out, uint32_t* memory_type_out,
|
||||
VkDeviceSize* memory_size_out) {
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const VulkanDevice* const vulkan_device, const VkDeviceSize size,
|
||||
const VkBufferUsageFlags usage, const MemoryPurpose memory_purpose,
|
||||
VkBuffer& buffer_out, VkDeviceMemory& memory_out,
|
||||
uint32_t* const memory_type_out, VkDeviceSize* const memory_size_out) {
|
||||
const VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
VkBufferCreateInfo buffer_create_info;
|
||||
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
|
|
@ -76,24 +76,23 @@ bool CreateDedicatedAllocationBuffer(
|
|||
|
||||
VkMemoryRequirements memory_requirements;
|
||||
dfn.vkGetBufferMemoryRequirements(device, buffer, &memory_requirements);
|
||||
uint32_t memory_type = ChooseMemoryType(
|
||||
provider, memory_requirements.memoryTypeBits, memory_purpose);
|
||||
uint32_t memory_type =
|
||||
ChooseMemoryType(vulkan_device->memory_types(),
|
||||
memory_requirements.memoryTypeBits, memory_purpose);
|
||||
if (memory_type == UINT32_MAX) {
|
||||
dfn.vkDestroyBuffer(device, buffer, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo memory_allocate_info;
|
||||
VkMemoryAllocateInfo* memory_allocate_info_last = &memory_allocate_info;
|
||||
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
memory_allocate_info.pNext = nullptr;
|
||||
memory_allocate_info.allocationSize = memory_requirements.size;
|
||||
memory_allocate_info.memoryTypeIndex = memory_type;
|
||||
VkMemoryDedicatedAllocateInfo memory_dedicated_allocate_info;
|
||||
if (provider.device_info().ext_1_1_VK_KHR_dedicated_allocation) {
|
||||
memory_allocate_info_last->pNext = &memory_dedicated_allocate_info;
|
||||
memory_allocate_info_last = reinterpret_cast<VkMemoryAllocateInfo*>(
|
||||
&memory_dedicated_allocate_info);
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_dedicated_allocation) {
|
||||
memory_dedicated_allocate_info.pNext = memory_allocate_info.pNext;
|
||||
memory_allocate_info.pNext = &memory_dedicated_allocate_info;
|
||||
memory_dedicated_allocate_info.sType =
|
||||
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
|
||||
memory_dedicated_allocate_info.pNext = nullptr;
|
||||
|
|
@ -124,15 +123,15 @@ bool CreateDedicatedAllocationBuffer(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CreateDedicatedAllocationImage(const VulkanProvider& provider,
|
||||
bool CreateDedicatedAllocationImage(const VulkanDevice* const vulkan_device,
|
||||
const VkImageCreateInfo& create_info,
|
||||
MemoryPurpose memory_purpose,
|
||||
const MemoryPurpose memory_purpose,
|
||||
VkImage& image_out,
|
||||
VkDeviceMemory& memory_out,
|
||||
uint32_t* memory_type_out,
|
||||
VkDeviceSize* memory_size_out) {
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
uint32_t* const memory_type_out,
|
||||
VkDeviceSize* const memory_size_out) {
|
||||
const VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
|
||||
VkImage image;
|
||||
if (dfn.vkCreateImage(device, &create_info, nullptr, &image) != VK_SUCCESS) {
|
||||
|
|
@ -141,24 +140,23 @@ bool CreateDedicatedAllocationImage(const VulkanProvider& provider,
|
|||
|
||||
VkMemoryRequirements memory_requirements;
|
||||
dfn.vkGetImageMemoryRequirements(device, image, &memory_requirements);
|
||||
uint32_t memory_type = ChooseMemoryType(
|
||||
provider, memory_requirements.memoryTypeBits, memory_purpose);
|
||||
uint32_t memory_type =
|
||||
ChooseMemoryType(vulkan_device->memory_types(),
|
||||
memory_requirements.memoryTypeBits, memory_purpose);
|
||||
if (memory_type == UINT32_MAX) {
|
||||
dfn.vkDestroyImage(device, image, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo memory_allocate_info;
|
||||
VkMemoryAllocateInfo* memory_allocate_info_last = &memory_allocate_info;
|
||||
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
memory_allocate_info.pNext = nullptr;
|
||||
memory_allocate_info.allocationSize = memory_requirements.size;
|
||||
memory_allocate_info.memoryTypeIndex = memory_type;
|
||||
VkMemoryDedicatedAllocateInfo memory_dedicated_allocate_info;
|
||||
if (provider.device_info().ext_1_1_VK_KHR_dedicated_allocation) {
|
||||
memory_allocate_info_last->pNext = &memory_dedicated_allocate_info;
|
||||
memory_allocate_info_last = reinterpret_cast<VkMemoryAllocateInfo*>(
|
||||
&memory_dedicated_allocate_info);
|
||||
if (vulkan_device->extensions().ext_1_1_KHR_dedicated_allocation) {
|
||||
memory_dedicated_allocate_info.pNext = memory_allocate_info.pNext;
|
||||
memory_allocate_info.pNext = &memory_dedicated_allocate_info;
|
||||
memory_dedicated_allocate_info.sType =
|
||||
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
|
||||
memory_dedicated_allocate_info.pNext = nullptr;
|
||||
|
|
@ -190,11 +188,10 @@ bool CreateDedicatedAllocationImage(const VulkanProvider& provider,
|
|||
}
|
||||
|
||||
VkPipeline CreateComputePipeline(
|
||||
const VulkanProvider& provider, VkPipelineLayout layout,
|
||||
VkShaderModule shader, const VkSpecializationInfo* specialization_info,
|
||||
const char* entry_point) {
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
const VulkanDevice* const vulkan_device, const VkPipelineLayout layout,
|
||||
const VkShaderModule shader,
|
||||
const VkSpecializationInfo* const specialization_info,
|
||||
const char* const entry_point) {
|
||||
VkComputePipelineCreateInfo pipeline_create_info;
|
||||
pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
pipeline_create_info.pNext = nullptr;
|
||||
|
|
@ -211,28 +208,27 @@ VkPipeline CreateComputePipeline(
|
|||
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
|
||||
pipeline_create_info.basePipelineIndex = -1;
|
||||
VkPipeline pipeline;
|
||||
if (dfn.vkCreateComputePipelines(device, VK_NULL_HANDLE, 1,
|
||||
&pipeline_create_info, nullptr,
|
||||
&pipeline) != VK_SUCCESS) {
|
||||
if (vulkan_device->functions().vkCreateComputePipelines(
|
||||
vulkan_device->device(), VK_NULL_HANDLE, 1, &pipeline_create_info,
|
||||
nullptr, &pipeline) != VK_SUCCESS) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VkPipeline CreateComputePipeline(
|
||||
const VulkanProvider& provider, VkPipelineLayout layout,
|
||||
const VulkanDevice* const vulkan_device, VkPipelineLayout layout,
|
||||
const uint32_t* shader_code, size_t shader_code_size_bytes,
|
||||
const VkSpecializationInfo* specialization_info, const char* entry_point) {
|
||||
VkShaderModule shader =
|
||||
CreateShaderModule(provider, shader_code, shader_code_size_bytes);
|
||||
const VkShaderModule shader =
|
||||
CreateShaderModule(vulkan_device, shader_code, shader_code_size_bytes);
|
||||
if (shader == VK_NULL_HANDLE) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
VkPipeline pipeline = CreateComputePipeline(provider, layout, shader,
|
||||
specialization_info, entry_point);
|
||||
dfn.vkDestroyShaderModule(device, shader, nullptr);
|
||||
const VkPipeline pipeline = CreateComputePipeline(
|
||||
vulkan_device, layout, shader, specialization_info, entry_point);
|
||||
vulkan_device->functions().vkDestroyShaderModule(vulkan_device->device(),
|
||||
shader, nullptr);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,15 +14,16 @@
|
|||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/vulkan/vulkan_device.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
namespace vulkan {
|
||||
namespace util {
|
||||
|
||||
template <typename F, typename T>
|
||||
inline bool DestroyAndNullHandle(F* destroy_function, T& handle) {
|
||||
template <typename DestroyFunction, typename Object>
|
||||
inline bool DestroyAndNullHandle(DestroyFunction* const destroy_function,
|
||||
Object& handle) {
|
||||
if (handle != VK_NULL_HANDLE) {
|
||||
destroy_function(handle, nullptr);
|
||||
handle = VK_NULL_HANDLE;
|
||||
|
|
@ -31,8 +32,9 @@ inline bool DestroyAndNullHandle(F* destroy_function, T& handle) {
|
|||
return false;
|
||||
}
|
||||
|
||||
template <typename F, typename P, typename T>
|
||||
inline bool DestroyAndNullHandle(F* destroy_function, P parent, T& handle) {
|
||||
template <typename DestroyFunction, typename Parent, typename Object>
|
||||
inline bool DestroyAndNullHandle(DestroyFunction* const destroy_function,
|
||||
const Parent parent, Object& handle) {
|
||||
if (handle != VK_NULL_HANDLE) {
|
||||
destroy_function(parent, handle, nullptr);
|
||||
handle = VK_NULL_HANDLE;
|
||||
|
|
@ -47,28 +49,17 @@ enum class MemoryPurpose {
|
|||
kReadback,
|
||||
};
|
||||
|
||||
inline VkDeviceSize GetMappableMemorySize(const VulkanProvider& provider,
|
||||
VkDeviceSize size) {
|
||||
VkDeviceSize non_coherent_atom_size =
|
||||
provider.device_info().nonCoherentAtomSize;
|
||||
// On some Android implementations, nonCoherentAtomSize is 0, not 1.
|
||||
if (non_coherent_atom_size > 1) {
|
||||
size = xe::round_up(size, non_coherent_atom_size, false);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
inline uint32_t ChooseHostMemoryType(const VulkanProvider& provider,
|
||||
uint32_t supported_types,
|
||||
bool is_readback) {
|
||||
supported_types &= provider.device_info().memory_types_host_visible;
|
||||
uint32_t host_cached = provider.device_info().memory_types_host_cached;
|
||||
inline uint32_t ChooseHostMemoryType(
|
||||
const VulkanDevice::MemoryTypes& memory_types, uint32_t supported_types,
|
||||
const bool is_readback) {
|
||||
supported_types &= memory_types.host_visible;
|
||||
uint32_t memory_type;
|
||||
// For upload, uncached is preferred so writes do not pollute the CPU cache.
|
||||
// For readback, cached is preferred so multiple CPU reads are fast.
|
||||
// If the preferred caching behavior is not available, pick any host-visible.
|
||||
if (xe::bit_scan_forward(
|
||||
supported_types & (is_readback ? host_cached : ~host_cached),
|
||||
supported_types & (is_readback ? memory_types.host_cached
|
||||
: ~memory_types.host_cached),
|
||||
&memory_type) ||
|
||||
xe::bit_scan_forward(supported_types, &memory_type)) {
|
||||
return memory_type;
|
||||
|
|
@ -76,18 +67,24 @@ inline uint32_t ChooseHostMemoryType(const VulkanProvider& provider,
|
|||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
inline uint32_t ChooseMemoryType(const VulkanProvider& provider,
|
||||
uint32_t supported_types,
|
||||
MemoryPurpose purpose) {
|
||||
inline uint32_t ChooseMemoryType(const VulkanDevice::MemoryTypes& memory_types,
|
||||
const uint32_t supported_types,
|
||||
const MemoryPurpose purpose) {
|
||||
switch (purpose) {
|
||||
case MemoryPurpose::kDeviceLocal: {
|
||||
uint32_t memory_type;
|
||||
return xe::bit_scan_forward(supported_types, &memory_type) ? memory_type
|
||||
: UINT32_MAX;
|
||||
if (xe::bit_scan_forward(supported_types & memory_types.device_local,
|
||||
&memory_type)) {
|
||||
return memory_type;
|
||||
}
|
||||
if (xe::bit_scan_forward(supported_types, &memory_type)) {
|
||||
return memory_type;
|
||||
}
|
||||
return UINT32_MAX;
|
||||
} break;
|
||||
case MemoryPurpose::kUpload:
|
||||
case MemoryPurpose::kReadback:
|
||||
return ChooseHostMemoryType(provider, supported_types,
|
||||
return ChooseHostMemoryType(memory_types, supported_types,
|
||||
purpose == MemoryPurpose::kReadback);
|
||||
default:
|
||||
assert_unhandled_case(purpose);
|
||||
|
|
@ -100,27 +97,28 @@ inline uint32_t ChooseMemoryType(const VulkanProvider& provider,
|
|||
// size (offset + size passed to vkFlushMappedMemoryRanges inside this function
|
||||
// must be either a multiple of nonCoherentAtomSize (but not exceeding the
|
||||
// memory size) or equal to the memory size).
|
||||
void FlushMappedMemoryRange(const VulkanProvider& provider,
|
||||
void FlushMappedMemoryRange(const VulkanDevice* vulkan_device,
|
||||
VkDeviceMemory memory, uint32_t memory_type,
|
||||
VkDeviceSize offset = 0,
|
||||
VkDeviceSize memory_size = VK_WHOLE_SIZE,
|
||||
VkDeviceSize size = VK_WHOLE_SIZE);
|
||||
|
||||
inline VkExtent2D GetMax2DFramebufferExtent(const VulkanProvider& provider) {
|
||||
const VulkanProvider::DeviceInfo& device_info = provider.device_info();
|
||||
inline VkExtent2D GetMax2DFramebufferExtent(
|
||||
const VulkanDevice::Properties& device_properties) {
|
||||
VkExtent2D max_extent;
|
||||
max_extent.width = std::min(device_info.maxFramebufferWidth,
|
||||
device_info.maxImageDimension2D);
|
||||
max_extent.height = std::min(device_info.maxFramebufferHeight,
|
||||
device_info.maxImageDimension2D);
|
||||
max_extent.width = std::min(device_properties.maxFramebufferWidth,
|
||||
device_properties.maxImageDimension2D);
|
||||
max_extent.height = std::min(device_properties.maxFramebufferHeight,
|
||||
device_properties.maxImageDimension2D);
|
||||
return max_extent;
|
||||
}
|
||||
|
||||
inline VkImageSubresourceRange InitializeSubresourceRange(
|
||||
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
uint32_t base_mip_level = 0, uint32_t level_count = VK_REMAINING_MIP_LEVELS,
|
||||
uint32_t base_array_layer = 0,
|
||||
uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS) {
|
||||
const VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
const uint32_t base_mip_level = 0,
|
||||
const uint32_t level_count = VK_REMAINING_MIP_LEVELS,
|
||||
const uint32_t base_array_layer = 0,
|
||||
const uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS) {
|
||||
VkImageSubresourceRange range;
|
||||
range.aspectMask = aspect_mask;
|
||||
range.baseMipLevel = base_mip_level;
|
||||
|
|
@ -133,13 +131,16 @@ inline VkImageSubresourceRange InitializeSubresourceRange(
|
|||
// Creates a buffer backed by a dedicated allocation. The allocation size will
|
||||
// NOT be aligned to nonCoherentAtomSize - if mapping or flushing not the whole
|
||||
// size, memory_size_out must be used for clamping the range.
|
||||
bool CreateDedicatedAllocationBuffer(
|
||||
const VulkanProvider& provider, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
MemoryPurpose memory_purpose, VkBuffer& buffer_out,
|
||||
VkDeviceMemory& memory_out, uint32_t* memory_type_out = nullptr,
|
||||
bool CreateDedicatedAllocationBuffer(const VulkanDevice* vulkan_device,
|
||||
VkDeviceSize size,
|
||||
VkBufferUsageFlags usage,
|
||||
MemoryPurpose memory_purpose,
|
||||
VkBuffer& buffer_out,
|
||||
VkDeviceMemory& memory_out,
|
||||
uint32_t* memory_type_out = nullptr,
|
||||
VkDeviceSize* memory_size_out = nullptr);
|
||||
|
||||
bool CreateDedicatedAllocationImage(const VulkanProvider& provider,
|
||||
bool CreateDedicatedAllocationImage(const VulkanDevice* vulkan_device,
|
||||
const VkImageCreateInfo& create_info,
|
||||
MemoryPurpose memory_purpose,
|
||||
VkImage& image_out,
|
||||
|
|
@ -149,9 +150,9 @@ bool CreateDedicatedAllocationImage(const VulkanProvider& provider,
|
|||
|
||||
// Explicitly accepting const uint32_t* to make sure attention is paid to the
|
||||
// alignment where this is called for safety on different host architectures.
|
||||
inline VkShaderModule CreateShaderModule(const VulkanProvider& provider,
|
||||
const uint32_t* code,
|
||||
size_t code_size_bytes) {
|
||||
inline VkShaderModule CreateShaderModule(
|
||||
const VulkanDevice* const vulkan_device, const uint32_t* const code,
|
||||
const size_t code_size_bytes) {
|
||||
VkShaderModuleCreateInfo shader_module_create_info;
|
||||
shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
shader_module_create_info.pNext = nullptr;
|
||||
|
|
@ -159,20 +160,20 @@ inline VkShaderModule CreateShaderModule(const VulkanProvider& provider,
|
|||
shader_module_create_info.codeSize = code_size_bytes;
|
||||
shader_module_create_info.pCode = code;
|
||||
VkShaderModule shader_module;
|
||||
return provider.dfn().vkCreateShaderModule(
|
||||
provider.device(), &shader_module_create_info, nullptr,
|
||||
return vulkan_device->functions().vkCreateShaderModule(
|
||||
vulkan_device->device(), &shader_module_create_info, nullptr,
|
||||
&shader_module) == VK_SUCCESS
|
||||
? shader_module
|
||||
: VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkPipeline CreateComputePipeline(
|
||||
const VulkanProvider& provider, VkPipelineLayout layout,
|
||||
const VulkanDevice* vulkan_device, VkPipelineLayout layout,
|
||||
VkShaderModule shader,
|
||||
const VkSpecializationInfo* specialization_info = nullptr,
|
||||
const char* entry_point = "main");
|
||||
VkPipeline CreateComputePipeline(
|
||||
const VulkanProvider& provider, VkPipelineLayout layout,
|
||||
const VulkanDevice* vulkan_device, VkPipelineLayout layout,
|
||||
const uint32_t* shader_code, size_t shader_code_size_bytes,
|
||||
const VkSpecializationInfo* specialization_info = nullptr,
|
||||
const char* entry_point = "main");
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class VulkanWindowDemoApp final : public WindowDemoApp {
|
|||
|
||||
std::unique_ptr<GraphicsProvider> VulkanWindowDemoApp::CreateGraphicsProvider()
|
||||
const {
|
||||
return VulkanProvider::Create(true);
|
||||
return VulkanProvider::Create(false, true);
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
|
|
|
|||
Loading…
Reference in a new issue