#include "Emulator/Graphics/GraphicsRender.h" #include "Kyty/Core/DbgAssert.h" #include "Kyty/Core/Threads.h" #include "Kyty/Core/Vector.h" #include "Kyty/Math/Rand.h" #include "Emulator/Config.h" #include "Emulator/Graphics/GraphicContext.h" #include "Emulator/Graphics/HardwareContext.h" #include "Emulator/Graphics/Objects/DepthStencilBuffer.h" #include "Emulator/Graphics/Objects/GpuMemory.h" #include "Emulator/Graphics/Objects/IndexBuffer.h" #include "Emulator/Graphics/Objects/Label.h" #include "Emulator/Graphics/Objects/RenderTexture.h" #include "Emulator/Graphics/Objects/StorageBuffer.h" #include "Emulator/Graphics/Objects/StorageTexture.h" #include "Emulator/Graphics/Objects/Texture.h" #include "Emulator/Graphics/Objects/VertexBuffer.h" #include "Emulator/Graphics/Objects/VideoOutBuffer.h" #include "Emulator/Graphics/Shader.h" #include "Emulator/Graphics/Tile.h" #include "Emulator/Graphics/Utils.h" #include "Emulator/Graphics/VideoOut.h" #include "Emulator/Graphics/Window.h" #include "Emulator/Kernel/EventQueue.h" #include "Emulator/Kernel/Pthread.h" #include "Emulator/Libs/Errno.h" #include "Emulator/Profiler.h" #include #include #ifdef KYTY_EMU_ENABLED namespace Kyty::Libs::Graphics { constexpr int GRAPHICS_EVENT_EOP = 0x40; struct Label; struct RenderDepthInfo; struct VulkanDescriptor { VkDescriptorSet descriptor_set = nullptr; }; #pragma pack(push, 1) struct PipelineStencilStaticState { VkStencilOp failOp = VK_STENCIL_OP_KEEP; VkStencilOp passOp = VK_STENCIL_OP_KEEP; VkStencilOp depthFailOp = VK_STENCIL_OP_KEEP; VkCompareOp compareOp = VK_COMPARE_OP_NEVER; }; struct PipelineStencilDynamicState { uint32_t compareMask = 0; uint32_t writeMask = 0; uint32_t reference = 0; }; struct PipelineStaticParameters { float viewport_scale[3] = {}; float viewport_offset[3] = {}; int scissor_ltrb[4] = {0}; VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; bool with_depth = false; bool depth_test_enable = false; bool depth_write_enable = false; VkCompareOp depth_compare_op = VK_COMPARE_OP_NEVER; bool depth_bounds_test_enable = false; float depth_min_bounds = 0.0f; float depth_max_bounds = 0.0f; bool stencil_test_enable = false; PipelineStencilStaticState stencil_front; PipelineStencilStaticState stencil_back; uint32_t color_mask = 0; bool cull_front = false; bool cull_back = false; bool face = false; uint8_t color_srcblend = 0; uint8_t color_comb_fcn = 0; uint8_t color_destblend = 0; uint8_t alpha_srcblend = 0; uint8_t alpha_comb_fcn = 0; uint8_t alpha_destblend = 0; bool separate_alpha_blend = false; bool blend_enable = false; float blend_color_red = 0.0f; float blend_color_green = 0.0f; float blend_color_blue = 0.0f; float blend_color_alpha = 0.0f; bool operator==(const PipelineStaticParameters& other) const; }; struct PipelineDynamicParameters { bool vk_dynamic_state_stencil_compare_mask = false; bool vk_dynamic_state_stencil_write_mask = false; bool vk_dynamic_state_stencil_reference = false; PipelineStencilDynamicState stencil_front; PipelineStencilDynamicState stencil_back; bool operator==(const PipelineDynamicParameters& other) const; }; #pragma pack(pop) struct PipelineAdditionalParameters { ShaderBindParameters vs_bind; ShaderBindParameters ps_bind; ShaderBindParameters cs_bind; }; struct VulkanPipeline { VkPipelineLayout pipeline_layout = nullptr; VkPipeline pipeline = nullptr; const PipelineStaticParameters* static_params = nullptr; PipelineDynamicParameters* dynamic_params = nullptr; const PipelineAdditionalParameters* additional_params = nullptr; }; class PipelineCache { public: PipelineCache() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); } virtual ~PipelineCache() { KYTY_NOT_IMPLEMENTED; } KYTY_CLASS_NO_COPY(PipelineCache); VulkanPipeline* CreatePipeline(VulkanFramebuffer* framebuffer, RenderColorInfo* color, RenderDepthInfo* depth, const ShaderVertexInputInfo* vs_input_info, const VertexShaderInfo* vs_regs, const ShaderPixelInputInfo* ps_input_info, const PixelShaderInfo* ps_regs, VkPrimitiveTopology topology, uint32_t color_mask, const ModeControl& mc, const BlendControl& bc, const BlendColor& bclr, const ScreenViewport& vp); VulkanPipeline* CreatePipeline(const ShaderComputeInputInfo* input_info, const ComputeShaderInfo* cs_regs); void DeletePipeline(VulkanPipeline* pipeline); void DeletePipelines(VulkanFramebuffer* framebuffer); void DeleteAllPipelines(); private: static constexpr uint32_t MAX_PIPELINES = 32; struct Pipeline { uint64_t render_pass_id = 0; ShaderId vs_shader_id; ShaderId ps_shader_id; ShaderId cs_shader_id; VulkanPipeline* pipeline = nullptr; PipelineStaticParameters* static_params = nullptr; PipelineDynamicParameters* dynamic_params = nullptr; }; [[nodiscard]] VulkanPipeline* Find(const Pipeline& p) const; static void DeletePipelineInternal(Pipeline& p); Vector m_pipelines; Core::Mutex m_mutex; }; struct VulkanDescriptorSet { VkDescriptorSet set = nullptr; VkDescriptorPool pool = nullptr; VkDescriptorSetLayout layout = nullptr; }; class DescriptorCache { public: enum class Stage { Unknown, Vertex, Pixel, Compute }; static constexpr int BUFFERS_MAX = ShaderStorageResources::BUFFERS_MAX; static constexpr int TEXTURES_SAMPLED_MAX = ShaderTextureResources::RES_MAX; static constexpr int TEXTURES_STORAGE_MAX = ShaderTextureResources::RES_MAX; static constexpr int SAMPLERS_MAX = ShaderSamplerResources::RES_MAX; static constexpr int PUSH_CONSTANTS_MAX = 16 * 4; static constexpr int GDS_BUFFER_MAX = 1; DescriptorCache() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); } virtual ~DescriptorCache() { KYTY_NOT_IMPLEMENTED; } KYTY_CLASS_NO_COPY(DescriptorCache); VkDescriptorSetLayout GetDescriptorSetLayout(Stage stage, const ShaderBindResources& bind, const ShaderBindParameters& bind_params); VulkanDescriptorSet* Allocate(Stage stage, int storage_buffers_num, int textures2d_sampled_num, int textures2d_storage_num, int samplers_num, int gds_buffers_num); void Free(VulkanDescriptorSet* set); VulkanDescriptorSet* GetDescriptor(Stage stage, VulkanBuffer** storage_buffers, VulkanImage** textures2d_sampled, VulkanImage** textures2d_storage, uint64_t* samplers, VulkanBuffer** gds_buffers, const ShaderBindResources& bind, const ShaderBindParameters& bind_params); void FreeDescriptor(VulkanBuffer* buffer); void FreeDescriptor(VulkanImage* image); private: struct Set { VulkanDescriptorSet* set = nullptr; Stage stage = Stage::Unknown; int storage_buffers_num = 0; uint64_t storage_buffers_id[BUFFERS_MAX] = {}; int textures2d_sampled_num = 0; uint64_t textures2d_sampled_id[TEXTURES_SAMPLED_MAX] = {}; int textures2d_storage_num = 0; uint64_t textures2d_storage_id[TEXTURES_STORAGE_MAX] = {}; int samplers_num = 0; uint64_t samplers_id[SAMPLERS_MAX] = {}; int gds_buffers_num = 0; uint64_t gds_buffers_id[GDS_BUFFER_MAX] = {}; }; void Init(); void CreatePool(); Core::Mutex m_mutex; Vector m_pools; Vector m_sets; VkDescriptorSetLayout m_descriptor_set_layout_vertex[BUFFERS_MAX + 1][TEXTURES_SAMPLED_MAX + 1][TEXTURES_STORAGE_MAX + 1] [SAMPLERS_MAX + 1][GDS_BUFFER_MAX + 1] = {}; VkDescriptorSetLayout m_descriptor_set_layout_pixel[BUFFERS_MAX + 1][TEXTURES_SAMPLED_MAX + 1][TEXTURES_STORAGE_MAX + 1] [SAMPLERS_MAX + 1][GDS_BUFFER_MAX + 1] = {}; VkDescriptorSetLayout m_descriptor_set_layout_compute[BUFFERS_MAX + 1][TEXTURES_SAMPLED_MAX + 1][TEXTURES_STORAGE_MAX + 1] [SAMPLERS_MAX + 1][GDS_BUFFER_MAX + 1] = {}; bool m_initialized = false; }; class SamplerCache { public: SamplerCache() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); } virtual ~SamplerCache() { KYTY_NOT_IMPLEMENTED; } KYTY_CLASS_NO_COPY(SamplerCache); VkSampler GetSampler(uint64_t id); uint64_t GetSamplerId(const ShaderSamplerResource& r); private: struct Sampler { ShaderSamplerResource r; VkSampler vk = nullptr; }; Core::Mutex m_mutex; Vector m_samplers; }; struct VulkanFramebuffer { VkRenderPass render_pass = nullptr; uint64_t render_pass_id = 0; VkFramebuffer framebuffer = nullptr; }; class FramebufferCache { public: FramebufferCache() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); } virtual ~FramebufferCache() { KYTY_NOT_IMPLEMENTED; } KYTY_CLASS_NO_COPY(FramebufferCache); VulkanFramebuffer* CreateFramebuffer(RenderColorInfo* color, RenderDepthInfo* depth); void FreeFramebufferByColor(VulkanImage* image); void FreeFramebufferByDepth(DepthStencilVulkanImage* image); private: VideoOutVulkanImage* CreateDummyBuffer(VkFormat format, uint32_t width, uint32_t height); struct Framebuffer { VulkanFramebuffer* framebuffer = nullptr; uint64_t image_id = 0; uint64_t depth_id = 0; bool depth_clear_enable = false; bool stencil_clear_enable = false; }; Core::Mutex m_mutex; Vector m_framebuffers; Vector m_dummy_buffers; }; class GdsBuffer { public: GdsBuffer() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); } virtual ~GdsBuffer() { KYTY_NOT_IMPLEMENTED; } KYTY_CLASS_NO_COPY(GdsBuffer); void Clear(GraphicContext* ctx, uint64_t dw_offset, uint32_t dw_num, uint32_t clear_value); void Read(GraphicContext* ctx, uint32_t* dst, uint32_t dw_offset, uint32_t dw_size); VulkanBuffer* GetBuffer(GraphicContext* ctx); private: static constexpr uint64_t DW_SIZE = 0x3000; void Init(GraphicContext* ctx); Core::Mutex m_mutex; VulkanBuffer* m_buffer = nullptr; }; class RenderContext { public: RenderContext() : m_pipeline_cache(new PipelineCache), m_descriptor_cache(new DescriptorCache), m_framebuffer_cache(new FramebufferCache), m_sampler_cache(new SamplerCache), m_gds_buffer(new GdsBuffer) { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); } virtual ~RenderContext() { KYTY_NOT_IMPLEMENTED; } KYTY_CLASS_NO_COPY(RenderContext); void SetGraphicCtx(GraphicContext* ctx) { m_graphic_ctx = ctx; } GraphicContext* GetGraphicCtx() { return m_graphic_ctx; } Core::Mutex& GetMutex() { return m_mutex; } PipelineCache* GetPipelineCache() { return m_pipeline_cache; } DescriptorCache* GetDescriptorCache() { return m_descriptor_cache; } FramebufferCache* GetFramebufferCache() { return m_framebuffer_cache; } SamplerCache* GetSamplerCache() { return m_sampler_cache; } GdsBuffer* GetGdsBuffer() { return m_gds_buffer; } //[[nodiscard]] LibKernel::EventQueue::KernelEqueue GetEopEq() const { return m_eop_eq; } // void SetEopEq(LibKernel::EventQueue::KernelEqueue eop_eq) { m_eop_eq = eop_eq; } void AddEopEq(LibKernel::EventQueue::KernelEqueue eq); void DeleteEopEq(LibKernel::EventQueue::KernelEqueue eq); void TriggerEopEvent(); private: Core::Mutex m_mutex; PipelineCache* m_pipeline_cache = nullptr; DescriptorCache* m_descriptor_cache = nullptr; FramebufferCache* m_framebuffer_cache = nullptr; SamplerCache* m_sampler_cache = nullptr; GraphicContext* m_graphic_ctx = nullptr; GdsBuffer* m_gds_buffer = nullptr; Core::Mutex m_eop_mutex; Vector m_eop_eqs; }; struct RenderDepthInfo { VkFormat format = VK_FORMAT_UNDEFINED; uint32_t width = 0; uint32_t height = 0; bool htile = false; bool neo = false; uint64_t depth_buffer_size = 0; uint64_t depth_buffer_vaddr = 0; uint64_t stencil_buffer_size = 0; uint64_t stencil_buffer_vaddr = 0; uint64_t htile_buffer_size = 0; uint64_t htile_buffer_vaddr = 0; bool depth_clear_enable = false; float depth_clear_value = 0.0f; bool depth_test_enable = false; bool depth_write_enable = false; VkCompareOp depth_compare_op = VK_COMPARE_OP_NEVER; bool depth_bounds_test_enable = false; float depth_min_bounds = 0.0f; float depth_max_bounds = 0.0f; bool stencil_clear_enable = false; uint8_t stencil_clear_value = 0; bool stencil_test_enable = false; PipelineStencilStaticState stencil_static_front; PipelineStencilStaticState stencil_static_back; PipelineStencilDynamicState stencil_dynamic_front; PipelineStencilDynamicState stencil_dynamic_back; DepthStencilVulkanImage* vulkan_buffer = nullptr; uint64_t vaddr[3] = {}; uint64_t size[3] = {}; int vaddr_num = 0; }; enum class RenderColorType { NoColorOutput, DisplayBuffer, OffscreenBuffer, RenderTexture, }; struct RenderColorInfo { RenderColorType type = RenderColorType::NoColorOutput; VulkanImage* vulkan_buffer = nullptr; uint64_t base_addr = 0; uint64_t buffer_size = 0; }; class CommandPool { public: CommandPool() = default; ~CommandPool() { if (m_pool != nullptr) { // TODO(): check if destructor is called from std::_Exit() // Delete(); } } KYTY_CLASS_NO_COPY(CommandPool); VulkanCommandPool* GetPool() { if (m_pool == nullptr) { Create(); } return m_pool; } private: void Create(); void Delete(); VulkanCommandPool* m_pool = nullptr; }; static RenderContext* g_render_ctx = nullptr; static thread_local CommandPool g_command_pool; // NOLINTNEXTLINE(readability-function-cognitive-complexity) static void uc_print(const char* func, const UserConfig& uc) { printf("%s\n", func); printf("\t GetPrimType() = 0x%08" PRIx32 "\n", uc.GetPrimType()); } // void uc_check(const UserConfig& uc) //{ // EXIT_NOT_IMPLEMENTED(uc.GetPrimType() != 4); //} static void rt_print(const char* func, const RenderTarget& rt) { printf("%s\n", func); printf("\t base_addr = 0x%016" PRIx64 "\n", rt.base_addr); printf("\t pitch_div8_minus1 = 0x%08" PRIx32 "\n", rt.pitch_div8_minus1); printf("\t fmask_pitch_div8_minus1 = 0x%08" PRIx32 "\n", rt.fmask_pitch_div8_minus1); printf("\t slice_div64_minus1 = 0x%08" PRIx32 "\n", rt.slice_div64_minus1); printf("\t base_array_slice_index = 0x%08" PRIx32 "\n", rt.base_array_slice_index); printf("\t last_array_slice_index = 0x%08" PRIx32 "\n", rt.last_array_slice_index); printf("\t color_info.fmask_compression_enable = %s\n", rt.color_info.fmask_compression_enable ? "true" : "false"); printf("\t color_info.fmask_compression_mode = 0x%08" PRIx32 "\n", rt.color_info.fmask_compression_mode); printf("\t color_info.cmask_fast_clear_enable = %s\n", rt.color_info.cmask_fast_clear_enable ? "true" : "false"); printf("\t color_info.dcc_compression_enable = %s\n", rt.color_info.dcc_compression_enable ? "true" : "false"); printf("\t color_info.neo_mode = %s\n", rt.color_info.neo_mode ? "true" : "false"); printf("\t color_info.cmask_tile_mode = 0x%08" PRIx32 "\n", rt.color_info.cmask_tile_mode); printf("\t color_info.cmask_tile_mode_neo = 0x%08" PRIx32 "\n", rt.color_info.cmask_tile_mode_neo); printf("\t color_info.format = 0x%08" PRIx32 "\n", rt.color_info.format); printf("\t color_info.channel_type = 0x%08" PRIx32 "\n", rt.color_info.channel_type); printf("\t color_info.channel_order = 0x%08" PRIx32 "\n", rt.color_info.channel_order); printf("\t force_dest_alpha_to_one = %s\n", rt.force_dest_alpha_to_one ? "true" : "false"); printf("\t tile_mode = 0x%08" PRIx32 "\n", rt.tile_mode); printf("\t fmask_tile_mode = 0x%08" PRIx32 "\n", rt.fmask_tile_mode); printf("\t num_samples = 0x%08" PRIx32 "\n", rt.num_samples); printf("\t num_fragments = 0x%08" PRIx32 "\n", rt.num_fragments); printf("\t dcc_max_uncompressed_block_size = 0x%08" PRIx32 "\n", rt.dcc_max_uncompressed_block_size); printf("\t dcc_max_compressed_block_size = 0x%08" PRIx32 "\n", rt.dcc_max_compressed_block_size); printf("\t dcc_min_compressed_block_size = 0x%08" PRIx32 "\n", rt.dcc_min_compressed_block_size); printf("\t dcc_color_transform = 0x%08" PRIx32 "\n", rt.dcc_color_transform); printf("\t dcc_enable_overwrite_combiner = %s\n", rt.dcc_enable_overwrite_combiner ? "true" : "false"); printf("\t dcc_force_independent_blocks = %s\n", rt.dcc_force_independent_blocks ? "true" : "false"); printf("\t cmask_addr = 0x%016" PRIx64 "\n", rt.cmask_addr); printf("\t cmask_slice_minus1 = 0x%08" PRIx32 "\n", rt.cmask_slice_minus1); printf("\t fmask_addr = 0x%016" PRIx64 "\n", rt.fmask_addr); printf("\t fmask_slice_minus1 = 0x%08" PRIx32 "\n", rt.fmask_slice_minus1); printf("\t clear_color_word0 = 0x%08" PRIx32 "\n", rt.clear_color_word0); printf("\t clear_color_word1 = 0x%08" PRIx32 "\n", rt.clear_color_word1); printf("\t dcc_addr = 0x%016" PRIx64 "\n", rt.dcc_addr); printf("\t width = 0x%08" PRIx32 "\n", rt.width); printf("\t height = 0x%08" PRIx32 "\n", rt.height); } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static void rt_check(const RenderTarget& rt) { if (rt.base_addr != 0) { bool render_to_texture = (rt.tile_mode == 0x0d); // EXIT_NOT_IMPLEMENTED(rt.base_addr == 0); // EXIT_NOT_IMPLEMENTED(rt.pitch_div8_minus1 != 0x000000ef); // EXIT_NOT_IMPLEMENTED(rt.fmask_pitch_div8_minus1 != 0x000000ef); // EXIT_NOT_IMPLEMENTED(rt.slice_div64_minus1 != 0x000086ff); EXIT_NOT_IMPLEMENTED(rt.base_array_slice_index != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.last_array_slice_index != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.color_info.fmask_compression_enable != false); EXIT_NOT_IMPLEMENTED(rt.color_info.fmask_compression_mode != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.color_info.cmask_fast_clear_enable != false); EXIT_NOT_IMPLEMENTED(rt.color_info.dcc_compression_enable != false); EXIT_NOT_IMPLEMENTED(!render_to_texture && rt.color_info.neo_mode != Config::IsNeo()); EXIT_NOT_IMPLEMENTED(rt.color_info.cmask_tile_mode != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.color_info.cmask_tile_mode_neo != 0x00000000); // EXIT_NOT_IMPLEMENTED(rt.format != 0x0000000a); // EXIT_NOT_IMPLEMENTED(rt.channel_type != 0x00000006); // EXIT_NOT_IMPLEMENTED(rt.channel_order != 0x00000001); EXIT_NOT_IMPLEMENTED(rt.force_dest_alpha_to_one != false); // EXIT_NOT_IMPLEMENTED(rt.tile_mode != 0x0000000a); // EXIT_NOT_IMPLEMENTED(rt.fmask_tile_mode != 0x0000000a); EXIT_NOT_IMPLEMENTED(rt.num_samples != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.num_fragments != 0x00000000); // EXIT_NOT_IMPLEMENTED(rt.dcc_max_uncompressed_block_size != 0x00000002); EXIT_NOT_IMPLEMENTED(rt.dcc_max_compressed_block_size != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.dcc_min_compressed_block_size != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.dcc_color_transform != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.dcc_enable_overwrite_combiner != false); EXIT_NOT_IMPLEMENTED(rt.dcc_force_independent_blocks != false); EXIT_NOT_IMPLEMENTED(rt.cmask_addr != 0x0000000000000000); EXIT_NOT_IMPLEMENTED(rt.cmask_slice_minus1 != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.fmask_addr != 0x0000000000000000); EXIT_NOT_IMPLEMENTED(rt.fmask_slice_minus1 != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.clear_color_word0 != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.clear_color_word1 != 0x00000000); EXIT_NOT_IMPLEMENTED(rt.dcc_addr != 0x0000000000000000); // EXIT_NOT_IMPLEMENTED(rt.width != 0x00000780); // EXIT_NOT_IMPLEMENTED(rt.height != 0x00000438); } } static void z_print(const char* func, const DepthRenderTarget& z) { printf("%s\n", func); printf("\t z_info.format = 0x%08" PRIx32 "\n", z.z_info.format); printf("\t z_info.tile_mode_index = 0x%08" PRIx32 "\n", z.z_info.tile_mode_index); printf("\t z_info.num_samples = 0x%08" PRIx32 "\n", z.z_info.num_samples); printf("\t z_info.tile_surface_enable = %s\n", z.z_info.tile_surface_enable ? "true" : "false"); printf("\t z_info.expclear_enabled = %s\n", z.z_info.expclear_enabled ? "true" : "false"); printf("\t z_info.zrange_precision = 0x%08" PRIx32 "\n", z.z_info.zrange_precision); printf("\t stencil_info.format = 0x%08" PRIx32 "\n", z.stencil_info.format); printf("\t stencil_info.tile_stencil_disable = %s\n", z.stencil_info.tile_stencil_disable ? "true" : "false"); printf("\t stencil_info.expclear_enabled = %s\n", z.stencil_info.expclear_enabled ? "true" : "false"); printf("\t stencil_info.tile_mode_index = 0x%08" PRIx32 "\n", z.stencil_info.tile_mode_index); printf("\t stencil_info.tile_split = 0x%08" PRIx32 "\n", z.stencil_info.tile_split); printf("\t depth_info.addr5_swizzle_mask = 0x%08" PRIx32 "\n", z.depth_info.addr5_swizzle_mask); printf("\t depth_info.array_mode = 0x%08" PRIx32 "\n", z.depth_info.array_mode); printf("\t depth_info.pipe_config = 0x%08" PRIx32 "\n", z.depth_info.pipe_config); printf("\t depth_info.bank_width = 0x%08" PRIx32 "\n", z.depth_info.bank_width); printf("\t depth_info.bank_height = 0x%08" PRIx32 "\n", z.depth_info.bank_height); printf("\t depth_info.macro_tile_aspect = 0x%08" PRIx32 "\n", z.depth_info.macro_tile_aspect); printf("\t depth_info.num_banks = 0x%08" PRIx32 "\n", z.depth_info.num_banks); printf("\t depth_view.slice_start = 0x%08" PRIx32 "\n", z.depth_view.slice_start); printf("\t depth_view.slice_max = 0x%08" PRIx32 "\n", z.depth_view.slice_max); printf("\t htile_surface.linear = 0x%08" PRIx32 "\n", z.htile_surface.linear); printf("\t htile_surface.full_cache = 0x%08" PRIx32 "\n", z.htile_surface.full_cache); printf("\t htile_surface.htile_uses_preload_win = 0x%08" PRIx32 "\n", z.htile_surface.htile_uses_preload_win); printf("\t htile_surface.preload = 0x%08" PRIx32 "\n", z.htile_surface.preload); printf("\t htile_surface.prefetch_width = 0x%08" PRIx32 "\n", z.htile_surface.prefetch_width); printf("\t htile_surface.prefetch_height = 0x%08" PRIx32 "\n", z.htile_surface.prefetch_height); printf("\t htile_surface.dst_outside_zero_to_one = 0x%08" PRIx32 "\n", z.htile_surface.dst_outside_zero_to_one); printf("\t z_read_base_addr = 0x%016" PRIx64 "\n", z.z_read_base_addr); printf("\t stencil_read_base_addr = 0x%016" PRIx64 "\n", z.stencil_read_base_addr); printf("\t z_write_base_addr = 0x%016" PRIx64 "\n", z.z_write_base_addr); printf("\t stencil_write_base_addr = 0x%016" PRIx64 "\n", z.stencil_write_base_addr); printf("\t pitch_div8_minus1 = 0x%08" PRIx32 "\n", z.pitch_div8_minus1); printf("\t height_div8_minus1 = 0x%08" PRIx32 "\n", z.height_div8_minus1); printf("\t slice_div64_minus1 = 0x%08" PRIx32 "\n", z.slice_div64_minus1); printf("\t htile_data_base_addr = 0x%016" PRIx64 "\n", z.htile_data_base_addr); printf("\t width = 0x%08" PRIx32 "\n", z.width); printf("\t height = 0x%08" PRIx32 "\n", z.height); } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static void z_check(const DepthRenderTarget& z) { if (z.z_info.format == 0) { EXIT_NOT_IMPLEMENTED(z.z_info.format != 0); EXIT_NOT_IMPLEMENTED(z.z_info.tile_mode_index != 0); EXIT_NOT_IMPLEMENTED(z.z_info.num_samples != 0); EXIT_NOT_IMPLEMENTED(z.z_info.tile_surface_enable != false); EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false); EXIT_NOT_IMPLEMENTED(z.z_info.zrange_precision != 0); } else { EXIT_NOT_IMPLEMENTED(z.z_info.format != 0x00000003); // EXIT_NOT_IMPLEMENTED(z.z_info.tile_mode_index != (Config::IsNeo() ? 0x00000002 : 0)); EXIT_NOT_IMPLEMENTED(z.z_info.num_samples != 0x00000000); // EXIT_NOT_IMPLEMENTED(z.z_info.tile_surface_enable != true); EXIT_NOT_IMPLEMENTED(z.z_info.expclear_enabled != false); EXIT_NOT_IMPLEMENTED(z.z_info.zrange_precision != 0x00000001); } if (z.stencil_info.format == 0) { EXIT_NOT_IMPLEMENTED(z.stencil_info.format != 0); // EXIT_NOT_IMPLEMENTED(z.stencil_info.tile_stencil_disable != false); EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false); // EXIT_NOT_IMPLEMENTED(z.stencil_info.tile_mode_index != 0); // EXIT_NOT_IMPLEMENTED(z.stencil_info.tile_split != 0); } else { EXIT_NOT_IMPLEMENTED(z.stencil_info.format != 0x00000001); EXIT_NOT_IMPLEMENTED(z.stencil_info.tile_stencil_disable != true); EXIT_NOT_IMPLEMENTED(z.stencil_info.expclear_enabled != false); // EXIT_NOT_IMPLEMENTED(z.stencil_info.tile_mode_index != (Config::IsNeo() ? 0x00000002 : 0)); // EXIT_NOT_IMPLEMENTED(z.stencil_info.tile_split != (Config::IsNeo() ? 0x00000002 : 0)); } if (z.z_info.format != 0 || z.stencil_info.format != 0) { EXIT_NOT_IMPLEMENTED(z.depth_info.addr5_swizzle_mask != 0x00000001); EXIT_NOT_IMPLEMENTED(z.depth_info.array_mode != 0x00000004); EXIT_NOT_IMPLEMENTED(z.depth_info.pipe_config != (Config::IsNeo() ? 0x00000012 : 0x0c)); EXIT_NOT_IMPLEMENTED(z.depth_info.bank_width != 0x00000000); // EXIT_NOT_IMPLEMENTED(z.depth_info.bank_height != (Config::IsNeo() ? 0x00000001 : 2)); // EXIT_NOT_IMPLEMENTED(z.depth_info.macro_tile_aspect != (Config::IsNeo() ? 0x00000000 : 2)); // EXIT_NOT_IMPLEMENTED(z.depth_info.num_banks != (Config::IsNeo() ? 0x00000002 : 3)); EXIT_NOT_IMPLEMENTED(z.depth_view.slice_start != 0x00000000); EXIT_NOT_IMPLEMENTED(z.depth_view.slice_max != 0x00000000); EXIT_NOT_IMPLEMENTED(z.htile_surface.linear != 0x00000000); EXIT_NOT_IMPLEMENTED(z.htile_surface.full_cache != 0x00000000); EXIT_NOT_IMPLEMENTED(z.htile_surface.htile_uses_preload_win != 0x00000000); EXIT_NOT_IMPLEMENTED(z.htile_surface.preload != 0x00000001); EXIT_NOT_IMPLEMENTED(z.htile_surface.prefetch_width != 0x00000000); EXIT_NOT_IMPLEMENTED(z.htile_surface.prefetch_height != 0x00000000); EXIT_NOT_IMPLEMENTED(z.htile_surface.dst_outside_zero_to_one != 0x00000000); EXIT_NOT_IMPLEMENTED(z.z_read_base_addr != z.z_write_base_addr); EXIT_NOT_IMPLEMENTED(z.stencil_read_base_addr != z.stencil_write_base_addr); EXIT_NOT_IMPLEMENTED(z.z_write_base_addr == 0); EXIT_NOT_IMPLEMENTED(z.stencil_info.format != 0 && z.stencil_write_base_addr == 0); // EXIT_NOT_IMPLEMENTED(z.pitch_div8_minus1 != 0x000000ff); // EXIT_NOT_IMPLEMENTED(z.height_div8_minus1 != 0x0000008f); // EXIT_NOT_IMPLEMENTED(z.slice_div64_minus1 != 0x00008fff); // EXIT_NOT_IMPLEMENTED(z.htile_data_base_addr == 0); // EXIT_NOT_IMPLEMENTED(z.width != 0x00000780); // EXIT_NOT_IMPLEMENTED(z.height != 0x00000438); } } static void clip_print(const char* func, const ClipControl& c) { printf("%s\n", func); printf("\t user_clip_planes = 0x%08" PRIx32 "\n", c.user_clip_planes); printf("\t user_clip_plane_mode = 0x%08" PRIx32 "\n", c.user_clip_plane_mode); printf("\t clip_space = 0x%08" PRIx32 "\n", c.clip_space); printf("\t vertex_kill_mode = 0x%08" PRIx32 "\n", c.vertex_kill_mode); printf("\t min_z_clip_enable = 0x%08" PRIx32 "\n", c.min_z_clip_enable); printf("\t max_z_clip_enable = 0x%08" PRIx32 "\n", c.max_z_clip_enable); printf("\t user_clip_plane_negate_y = %s\n", c.user_clip_plane_negate_y ? "true" : "false"); printf("\t clip_enable = %s\n", c.clip_enable ? "true" : "false"); printf("\t user_clip_plane_cull_only = %s\n", c.user_clip_plane_cull_only ? "true" : "false"); printf("\t cull_on_clipping_error_disable = %s\n", c.cull_on_clipping_error_disable ? "true" : "false"); printf("\t linear_attribute_clip_enable = %s\n", c.linear_attribute_clip_enable ? "true" : "false"); printf("\t force_viewport_index_from_vs_enable = %s\n", c.force_viewport_index_from_vs_enable ? "true" : "false"); } static void clip_check(const ClipControl& c) { EXIT_NOT_IMPLEMENTED(c.user_clip_planes != 0); EXIT_NOT_IMPLEMENTED(c.user_clip_plane_mode != 0); EXIT_NOT_IMPLEMENTED(c.clip_space != 0); EXIT_NOT_IMPLEMENTED(c.vertex_kill_mode != 0); EXIT_NOT_IMPLEMENTED(c.min_z_clip_enable != 0); EXIT_NOT_IMPLEMENTED(c.max_z_clip_enable != 0); EXIT_NOT_IMPLEMENTED(c.user_clip_plane_negate_y != false); EXIT_NOT_IMPLEMENTED(c.clip_enable != false); EXIT_NOT_IMPLEMENTED(c.user_clip_plane_cull_only != false); EXIT_NOT_IMPLEMENTED(c.cull_on_clipping_error_disable != false); EXIT_NOT_IMPLEMENTED(c.linear_attribute_clip_enable != false); EXIT_NOT_IMPLEMENTED(c.force_viewport_index_from_vs_enable != false); } static void rc_print(const char* func, const RenderControl& c) { printf("%s\n", func); printf("\t depth_clear_enable = %s\n", c.depth_clear_enable ? "true" : "false"); printf("\t stencil_clear_enable = %s\n", c.stencil_clear_enable ? "true" : "false"); printf("\t resummarize_enable = %s\n", c.resummarize_enable ? "true" : "false"); printf("\t stencil_compress_disable = %s\n", c.stencil_compress_disable ? "true" : "false"); printf("\t depth_compress_disable = %s\n", c.depth_compress_disable ? "true" : "false"); printf("\t copy_centroid = %s\n", c.copy_centroid ? "true" : "false"); printf("\t copy_sample = %" PRIu8 "\n", c.copy_sample); } static void rc_check(const RenderControl& c) { // EXIT_NOT_IMPLEMENTED(c.depth_clear_enable != false); // EXIT_NOT_IMPLEMENTED(c.stencil_clear_enable != false); EXIT_NOT_IMPLEMENTED(c.resummarize_enable != false); EXIT_NOT_IMPLEMENTED(c.stencil_compress_disable != false); EXIT_NOT_IMPLEMENTED(c.depth_compress_disable != false); EXIT_NOT_IMPLEMENTED(c.copy_centroid != false); EXIT_NOT_IMPLEMENTED(c.copy_sample != 0); } static void mc_print(const char* func, const ModeControl& c) { printf("%s\n", func); printf("\t cull_front = %s\n", c.cull_front ? "true" : "false"); printf("\t cull_back = %s\n", c.cull_back ? "true" : "false"); printf("\t face = %s\n", c.face ? "true" : "false"); printf("\t poly_mode = %" PRIu8 "\n", c.poly_mode); printf("\t polymode_front_ptype = %" PRIu8 "\n", c.polymode_front_ptype); printf("\t polymode_back_ptype = %" PRIu8 "\n", c.polymode_back_ptype); printf("\t poly_offset_front_enable = %s\n", c.poly_offset_front_enable ? "true" : "false"); printf("\t poly_offset_back_enable = %s\n", c.poly_offset_back_enable ? "true" : "false"); printf("\t vtx_window_offset_enable = %s\n", c.vtx_window_offset_enable ? "true" : "false"); printf("\t provoking_vtx_last = %s\n", c.provoking_vtx_last ? "true" : "false"); printf("\t persp_corr_dis = %s\n", c.persp_corr_dis ? "true" : "false"); } static void mc_check(const ModeControl& c) { EXIT_NOT_IMPLEMENTED(c.cull_front != false); // EXIT_NOT_IMPLEMENTED(c.cull_back != false); EXIT_NOT_IMPLEMENTED(c.face != false); EXIT_NOT_IMPLEMENTED(c.poly_mode != 0); EXIT_NOT_IMPLEMENTED(c.polymode_front_ptype != 0 && c.polymode_front_ptype != 2); EXIT_NOT_IMPLEMENTED(c.polymode_back_ptype != 0 && c.polymode_back_ptype != 2); EXIT_NOT_IMPLEMENTED(c.poly_offset_front_enable != false); EXIT_NOT_IMPLEMENTED(c.poly_offset_back_enable != false); EXIT_NOT_IMPLEMENTED(c.vtx_window_offset_enable != false); EXIT_NOT_IMPLEMENTED(c.provoking_vtx_last != false); EXIT_NOT_IMPLEMENTED(c.persp_corr_dis != false); } static void bc_print(const char* func, const BlendControl& c, const BlendColor& color) { printf("%s\n", func); printf("\t color_srcblend = %" PRIu8 "\n", c.color_srcblend); printf("\t color_comb_fcn = %" PRIu8 "\n", c.color_comb_fcn); printf("\t color_destblend = %" PRIu8 "\n", c.color_destblend); printf("\t alpha_srcblend = %" PRIu8 "\n", c.alpha_srcblend); printf("\t alpha_comb_fcn = %" PRIu8 "\n", c.alpha_comb_fcn); printf("\t alpha_destblend = %" PRIu8 "\n", c.alpha_destblend); printf("\t separate_alpha_blend = %s\n", c.separate_alpha_blend ? "true" : "false"); printf("\t enable = %s\n", c.enable ? "true" : "false"); printf("\t red = %f\n", color.red); printf("\t green = %f\n", color.green); printf("\t blue = %f\n", color.blue); printf("\t alpha = %f\n", color.alpha); } static void bc_check(const BlendControl& c, const BlendColor& color) { // EXIT_NOT_IMPLEMENTED(c.color_srcblend != 0); EXIT_NOT_IMPLEMENTED(c.color_comb_fcn != 0); // EXIT_NOT_IMPLEMENTED(c.color_destblend != 0); EXIT_NOT_IMPLEMENTED(c.alpha_srcblend != 0); EXIT_NOT_IMPLEMENTED(c.alpha_comb_fcn != 0); EXIT_NOT_IMPLEMENTED(c.alpha_destblend != 0); EXIT_NOT_IMPLEMENTED(c.separate_alpha_blend != false); // EXIT_NOT_IMPLEMENTED(c.enable != false); EXIT_NOT_IMPLEMENTED(color.red != 0.0f); EXIT_NOT_IMPLEMENTED(color.green != 0.0f); EXIT_NOT_IMPLEMENTED(color.blue != 0.0f); EXIT_NOT_IMPLEMENTED(color.alpha != 0.0f); } static void d_print(const char* func, const DepthControl& c, const StencilControl& s, const StencilMask& sm) { printf("%s\n", func); printf("\t stencil_enable = %s\n", c.stencil_enable ? "true" : "false"); printf("\t z_enable = %s\n", c.z_enable ? "true" : "false"); printf("\t z_write_enable = %s\n", c.z_write_enable ? "true" : "false"); printf("\t depth_bounds_enable = %s\n", c.depth_bounds_enable ? "true" : "false"); printf("\t zfunc = %" PRIu8 "\n", c.zfunc); printf("\t backface_enable = %s\n", c.backface_enable ? "true" : "false"); printf("\t stencilfunc = %" PRIu8 "\n", c.stencilfunc); printf("\t stencilfunc_bf = %" PRIu8 "\n", c.stencilfunc_bf); printf("\t stencil_fail = %" PRIu8 "\n", s.stencil_fail); printf("\t stencil_zpass = %" PRIu8 "\n", s.stencil_zpass); printf("\t stencil_zfail = %" PRIu8 "\n", s.stencil_zfail); printf("\t stencil_fail_bf = %" PRIu8 "\n", s.stencil_fail_bf); printf("\t stencil_zpass_bf = %" PRIu8 "\n", s.stencil_zpass_bf); printf("\t stencil_zfail_bf = %" PRIu8 "\n", s.stencil_zfail_bf); printf("\t stencil_testval = %" PRIu8 "\n", sm.stencil_testval); printf("\t stencil_mask = %" PRIu8 "\n", sm.stencil_mask); printf("\t stencil_writemask = %" PRIu8 "\n", sm.stencil_writemask); printf("\t stencil_opval = %" PRIu8 "\n", sm.stencil_opval); printf("\t stencil_testval_bf = %" PRIu8 "\n", sm.stencil_testval_bf); printf("\t stencil_mask_bf = %" PRIu8 "\n", sm.stencil_mask_bf); printf("\t stencil_writemask_bf = %" PRIu8 "\n", sm.stencil_writemask_bf); printf("\t stencil_opval_bf = %" PRIu8 "\n", sm.stencil_opval_bf); } static void d_check(const DepthControl& c, const StencilControl& s, const StencilMask& /*sm*/) { // EXIT_NOT_IMPLEMENTED(c.stencil_enable != false); // EXIT_NOT_IMPLEMENTED(c.z_enable != false); // EXIT_NOT_IMPLEMENTED(c.z_write_enable != false); EXIT_NOT_IMPLEMENTED(c.depth_bounds_enable != false); // EXIT_NOT_IMPLEMENTED(c.zfunc != 0); EXIT_NOT_IMPLEMENTED(c.backface_enable != false); // EXIT_NOT_IMPLEMENTED(c.stencilfunc != 0); // EXIT_NOT_IMPLEMENTED(c.stencilfunc_bf != 0); // EXIT_NOT_IMPLEMENTED(s.stencil_fail != 0); // EXIT_NOT_IMPLEMENTED(s.stencil_zpass != 0); // EXIT_NOT_IMPLEMENTED(s.stencil_zfail != 0); EXIT_NOT_IMPLEMENTED(s.stencil_fail_bf != 0); EXIT_NOT_IMPLEMENTED(s.stencil_zpass_bf != 0); EXIT_NOT_IMPLEMENTED(s.stencil_zfail_bf != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_testval != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_mask != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_writemask != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_opval != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_testval_bf != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_mask_bf != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_writemask_bf != 0); // EXIT_NOT_IMPLEMENTED(sm.stencil_opval_bf != 0); } static void eqaa_print(const char* func, const EqaaControl& c) { printf("%s\n", func); printf("\t max_anchor_samples = %" PRIu8 "\n", c.max_anchor_samples); printf("\t ps_iter_samples = %" PRIu8 "\n", c.ps_iter_samples); printf("\t mask_export_num_samples = %" PRIu8 "\n", c.mask_export_num_samples); printf("\t alpha_to_mask_num_samples = %" PRIu8 "\n", c.alpha_to_mask_num_samples); printf("\t high_quality_intersections = %s\n", c.high_quality_intersections ? "true" : "false"); printf("\t incoherent_eqaa_reads = %s\n", c.incoherent_eqaa_reads ? "true" : "false"); printf("\t interpolate_comp_z = %s\n", c.interpolate_comp_z ? "true" : "false"); printf("\t static_anchor_associations = %s\n", c.static_anchor_associations ? "true" : "false"); } static void eqaa_check(const EqaaControl& c) { EXIT_NOT_IMPLEMENTED(c.max_anchor_samples != 0); EXIT_NOT_IMPLEMENTED(c.ps_iter_samples != 0); EXIT_NOT_IMPLEMENTED(c.mask_export_num_samples != 0); EXIT_NOT_IMPLEMENTED(c.alpha_to_mask_num_samples != 0); EXIT_NOT_IMPLEMENTED(c.high_quality_intersections != false); EXIT_NOT_IMPLEMENTED(c.incoherent_eqaa_reads != false); EXIT_NOT_IMPLEMENTED(c.interpolate_comp_z != false); EXIT_NOT_IMPLEMENTED(c.static_anchor_associations != false); } static void vp_print(const char* func, const ScreenViewport& vp) { printf("%s\n", func); printf("\t viewports[0].zmin = %f\n", vp.viewports[0].zmin); printf("\t viewports[0].zmax = %f\n", vp.viewports[0].zmax); printf("\t viewports[0].xscale = %f\n", vp.viewports[0].xscale); printf("\t viewports[0].xoffset = %f\n", vp.viewports[0].xoffset); printf("\t viewports[0].yscale = %f\n", vp.viewports[0].yscale); printf("\t viewports[0].yoffset = %f\n", vp.viewports[0].yoffset); printf("\t viewports[0].zscale = %f\n", vp.viewports[0].zscale); printf("\t viewports[0].zoffset = %f\n", vp.viewports[0].zoffset); printf("\t transform_control = 0x%08" PRIx32 "\n", vp.transform_control); printf("\t scissor_left = %d\n", vp.scissor_left); printf("\t scissor_top = %d\n", vp.scissor_top); printf("\t scissor_right = %d\n", vp.scissor_right); printf("\t scissor_bottom = %d\n", vp.scissor_bottom); printf("\t hw_offset_x = %u\n", vp.hw_offset_x); printf("\t hw_offset_y = %u\n", vp.hw_offset_y); printf("\t guard_band_horz_clip = %f\n", vp.guard_band_horz_clip); printf("\t guard_band_vert_clip = %f\n", vp.guard_band_vert_clip); printf("\t guard_band_horz_discard = %f\n", vp.guard_band_horz_discard); printf("\t guard_band_vert_discard = %f\n", vp.guard_band_vert_discard); } static void vp_check(const ScreenViewport& vp) { EXIT_NOT_IMPLEMENTED(vp.viewports[0].zmin != 0.000000); EXIT_NOT_IMPLEMENTED(vp.viewports[0].zmax != 1.000000); // EXIT_NOT_IMPLEMENTED(vp.viewports[0].xscale != 960.000000); // EXIT_NOT_IMPLEMENTED(vp.viewports[0].xoffset != 960.000000); // EXIT_NOT_IMPLEMENTED(vp.viewports[0].yscale != -540.000000); // EXIT_NOT_IMPLEMENTED(vp.viewports[0].yoffset != 540.000000); // EXIT_NOT_IMPLEMENTED(vp.viewports[0].zscale != 0.500000); // EXIT_NOT_IMPLEMENTED(vp.viewports[0].zoffset != 0.500000); EXIT_NOT_IMPLEMENTED(vp.transform_control != 1087); // EXIT_NOT_IMPLEMENTED(vp.scissor_left != 0); // EXIT_NOT_IMPLEMENTED(vp.scissor_top != 0); // EXIT_NOT_IMPLEMENTED(vp.scissor_right != 1920); // EXIT_NOT_IMPLEMENTED(vp.scissor_bottom != 1080); // EXIT_NOT_IMPLEMENTED(vp.hw_offset_x != 60); // EXIT_NOT_IMPLEMENTED(vp.hw_offset_y != 32); // EXIT_NOT_IMPLEMENTED(fabsf(vp.guard_band_horz_clip - 33.133327f) > 0.001f); // EXIT_NOT_IMPLEMENTED(fabsf(vp.guard_band_vert_clip - 59.629623f) > 0.001f); EXIT_NOT_IMPLEMENTED(vp.guard_band_horz_discard != 1.000000); EXIT_NOT_IMPLEMENTED(vp.guard_band_vert_discard != 1.000000); } static void hw_check(const HardwareContext& hw) { const auto& rt = hw.GetRenderTargets(0); const auto& bc = hw.GetBlendControl(0); const auto& bclr = hw.GetBlendColor(); const auto& vp = hw.GetScreenViewport(); const auto& z = hw.GetDepthRenderTarget(); const auto& c = hw.GetClipControl(); const auto& rc = hw.GetRenderControl(); const auto& d = hw.GetDepthControl(); const auto& s = hw.GetStencilControl(); const auto& sm = hw.GetStencilMask(); const auto& mc = hw.GetModeControl(); const auto& eqaa = hw.GetEqaaControl(); rt_check(rt); vp_check(vp); z_check(z); clip_check(c); rc_check(rc); d_check(d, s, sm); mc_check(mc); bc_check(bc, bclr); eqaa_check(eqaa); EXIT_NOT_IMPLEMENTED(hw.GetRenderTargetMask() != 0xF && hw.GetRenderTargetMask() != 0x0); EXIT_NOT_IMPLEMENTED(hw.GetDepthClearValue() != 0.0f && hw.GetDepthClearValue() != 1.0f); // EXIT_NOT_IMPLEMENTED(hw.GetStencilClearValue() != 0); } static void hw_print(const HardwareContext& hw) { const auto& rt = hw.GetRenderTargets(0); const auto& bc = hw.GetBlendControl(0); const auto& bclr = hw.GetBlendColor(); const auto& vp = hw.GetScreenViewport(); const auto& z = hw.GetDepthRenderTarget(); const auto& c = hw.GetClipControl(); const auto& rc = hw.GetRenderControl(); const auto& d = hw.GetDepthControl(); const auto& s = hw.GetStencilControl(); const auto& sm = hw.GetStencilMask(); const auto& mc = hw.GetModeControl(); const auto& eqaa = hw.GetEqaaControl(); printf("HardwareContext\n"); printf("\t GetRenderTargetMask() = 0x%08" PRIx32 "\n", hw.GetRenderTargetMask()); printf("\t GetDepthClearValue() = %f\n", hw.GetDepthClearValue()); printf("\t GetStencilClearValue() = %" PRIu8 "\n", hw.GetStencilClearValue()); rt_print("RenderTraget:", rt); z_print("DepthRenderTraget:", z); vp_print("ScreenViewport:", vp); clip_print("ClipControl:", c); rc_print("RenderControl:", rc); d_print("DepthStencilControlMask:", d, s, sm); mc_print("ModeControl:", mc); bc_print("BlendControl:", bc, bclr); eqaa_print("EqaaControl:", eqaa); } void GraphicsRenderInit() { EXIT_IF(g_render_ctx != nullptr); g_render_ctx = new RenderContext; } void GraphicsRenderCreateContext() { EXIT_IF(g_render_ctx == nullptr); g_render_ctx->SetGraphicCtx(WindowGetGraphicContext()); } void RenderContext::AddEopEq(LibKernel::EventQueue::KernelEqueue eq) { Core::LockGuard lock(m_eop_mutex); EXIT_NOT_IMPLEMENTED(m_eop_eqs.Contains(eq)); m_eop_eqs.Add(eq); } void RenderContext::DeleteEopEq(LibKernel::EventQueue::KernelEqueue eq) { Core::LockGuard lock(m_eop_mutex); auto index = m_eop_eqs.Find(eq); EXIT_NOT_IMPLEMENTED(!m_eop_eqs.IndexValid(index)); m_eop_eqs[index] = nullptr; } void RenderContext::TriggerEopEvent() { Core::LockGuard lock(m_eop_mutex); for (auto& eop_eq: m_eop_eqs) { if (eop_eq != nullptr) { auto result = LibKernel::EventQueue::KernelTriggerEvent(eop_eq, GRAPHICS_EVENT_EOP, LibKernel::EventQueue::KERNEL_EVFILT_GRAPHICS, reinterpret_cast(LibKernel::KernelReadTsc())); EXIT_NOT_IMPLEMENTED(result != OK); } } } void GdsBuffer::Init(GraphicContext* ctx) { if (m_buffer == nullptr) { m_buffer = new VulkanBuffer; m_buffer->usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; m_buffer->memory.property = static_cast(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; m_buffer->buffer = nullptr; VulkanCreateBuffer(ctx, DW_SIZE * 4, m_buffer); EXIT_NOT_IMPLEMENTED(m_buffer->buffer == nullptr); } } void GdsBuffer::Clear(GraphicContext* ctx, uint64_t dw_offset, uint32_t dw_num, uint32_t clear_value) { EXIT_IF(ctx == nullptr); Core::LockGuard lock(m_mutex); Init(ctx); EXIT_NOT_IMPLEMENTED(dw_offset >= DW_SIZE); EXIT_NOT_IMPLEMENTED(dw_offset + dw_num > DW_SIZE); EXIT_IF(m_buffer == nullptr); void* data = nullptr; VulkanMapMemory(ctx, &m_buffer->memory, &data); EXIT_IF(data == nullptr); for (uint32_t i = 0; i < dw_num; i++) { static_cast(data)[dw_offset + i] = clear_value; } VulkanUnmapMemory(ctx, &m_buffer->memory); } void GdsBuffer::Read(GraphicContext* ctx, uint32_t* dst, uint32_t dw_offset, uint32_t dw_size) { EXIT_IF(dst == nullptr); Core::LockGuard lock(m_mutex); Init(ctx); EXIT_NOT_IMPLEMENTED(dw_offset >= DW_SIZE); EXIT_NOT_IMPLEMENTED(dw_offset + dw_size > DW_SIZE); EXIT_IF(m_buffer == nullptr); void* data = nullptr; VulkanMapMemory(ctx, &m_buffer->memory, &data); EXIT_IF(data == nullptr); for (uint32_t i = 0; i < dw_size; i++) { dst[i] = static_cast(data)[dw_offset + i]; } VulkanUnmapMemory(ctx, &m_buffer->memory); } VulkanBuffer* GdsBuffer::GetBuffer(GraphicContext* ctx) { Core::LockGuard lock(m_mutex); Init(ctx); return m_buffer; } void CommandPool::Create() { auto* ctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(m_pool != nullptr); m_pool = new VulkanCommandPool; EXIT_IF(ctx == nullptr); EXIT_IF(ctx->device == nullptr); EXIT_IF(ctx->queue_family_index == static_cast(-1)); EXIT_IF(m_pool->pool != nullptr); EXIT_IF(m_pool->buffers != nullptr); EXIT_IF(m_pool->fences != nullptr); EXIT_IF(m_pool->semaphores != nullptr); EXIT_IF(m_pool->buffers_count != 0); VkCommandPoolCreateInfo pool_info {}; pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; pool_info.pNext = nullptr; pool_info.queueFamilyIndex = ctx->queue_family_index; pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; vkCreateCommandPool(ctx->device, &pool_info, nullptr, &m_pool->pool); EXIT_NOT_IMPLEMENTED(m_pool->pool == nullptr); m_pool->buffers_count = 4; m_pool->buffers = new VkCommandBuffer[m_pool->buffers_count]; m_pool->fences = new VkFence[m_pool->buffers_count]; m_pool->semaphores = new VkSemaphore[m_pool->buffers_count]; m_pool->busy = new bool[m_pool->buffers_count]; VkCommandBufferAllocateInfo alloc_info {}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.commandPool = m_pool->pool; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandBufferCount = m_pool->buffers_count; if (vkAllocateCommandBuffers(ctx->device, &alloc_info, m_pool->buffers) != VK_SUCCESS) { EXIT("Can't allocate command buffers"); } for (uint32_t i = 0; i < m_pool->buffers_count; i++) { m_pool->busy[i] = false; VkFenceCreateInfo fence_info {}; fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence_info.pNext = nullptr; fence_info.flags = 0; if (vkCreateFence(ctx->device, &fence_info, nullptr, &m_pool->fences[i]) != VK_SUCCESS) { EXIT("Can't create fence"); } VkSemaphoreCreateInfo semaphore_info {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.pNext = nullptr; semaphore_info.flags = 0; if (vkCreateSemaphore(ctx->device, &semaphore_info, nullptr, &m_pool->semaphores[i]) != VK_SUCCESS) { EXIT("Can't create semaphore"); } EXIT_IF(m_pool->buffers[i] == nullptr); EXIT_IF(m_pool->fences[i] == nullptr); EXIT_IF(m_pool->semaphores[i] == nullptr); } } void CommandPool::Delete() { auto* ctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(m_pool == nullptr); EXIT_IF(ctx == nullptr); EXIT_IF(ctx->device == nullptr); for (uint32_t i = 0; i < m_pool->buffers_count; i++) { vkDestroySemaphore(ctx->device, m_pool->semaphores[i], nullptr); vkDestroyFence(ctx->device, m_pool->fences[i], nullptr); } vkFreeCommandBuffers(ctx->device, m_pool->pool, m_pool->buffers_count, m_pool->buffers); vkDestroyCommandPool(ctx->device, m_pool->pool, nullptr); delete[] m_pool->semaphores; delete[] m_pool->fences; delete[] m_pool->buffers; delete[] m_pool->busy; delete m_pool; m_pool = nullptr; } VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* color, RenderDepthInfo* depth) { KYTY_PROFILER_FUNCTION(); static std::atomic seq = 0; Core::LockGuard lock(m_mutex); EXIT_IF(color == nullptr); EXIT_IF(depth == nullptr); EXIT_IF(g_render_ctx == nullptr); bool with_depth = (depth->format != VK_FORMAT_UNDEFINED && depth->vulkan_buffer != nullptr); bool with_color = (color->vulkan_buffer != nullptr); for (auto& f: m_framebuffers) { if (f.framebuffer != nullptr && f.image_id == (with_color ? color->vulkan_buffer->memory.unique_id : 0) && f.depth_id == (with_depth ? depth->vulkan_buffer->memory.unique_id : 0) && f.depth_clear_enable == depth->depth_clear_enable && f.stencil_clear_enable == depth->stencil_clear_enable) { return f.framebuffer; } } auto* framebuffer = new VulkanFramebuffer; framebuffer->render_pass = nullptr; framebuffer->framebuffer = nullptr; auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); EXIT_NOT_IMPLEMENTED(!with_depth && !with_color); EXIT_NOT_IMPLEMENTED(with_depth && with_color && (color->vulkan_buffer->extent.width != depth->vulkan_buffer->extent.width || color->vulkan_buffer->extent.height != depth->vulkan_buffer->extent.height)); VulkanImage* vulkan_buffer = (with_color ? color->vulkan_buffer : CreateDummyBuffer(VK_FORMAT_B8G8R8A8_SRGB, depth->vulkan_buffer->extent.width, depth->vulkan_buffer->extent.height)); VkAttachmentDescription attachments[2]; attachments[0].flags = 0; attachments[0].format = vulkan_buffer->format; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachments[1].flags = 0; attachments[1].format = depth->format; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = (depth->depth_clear_enable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD); attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[1].stencilLoadOp = (depth->stencil_clear_enable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD); attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference color_attachment_ref {}; color_attachment_ref.attachment = (with_color ? 0 : VK_ATTACHMENT_UNUSED); color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference depth_attachment_ref {}; depth_attachment_ref.attachment = 1; depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass {}; subpass.flags = 0; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.inputAttachmentCount = 0; subpass.pInputAttachments = nullptr; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_attachment_ref; subpass.pResolveAttachments = nullptr; subpass.pDepthStencilAttachment = (with_depth ? &depth_attachment_ref : nullptr); subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; VkRenderPassCreateInfo render_pass_info {}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_info.pNext = nullptr; render_pass_info.flags = 0; render_pass_info.attachmentCount = (with_depth ? 2 : 1); render_pass_info.pAttachments = attachments; render_pass_info.subpassCount = 1; render_pass_info.pSubpasses = &subpass; render_pass_info.dependencyCount = 0; render_pass_info.pDependencies = nullptr; vkCreateRenderPass(gctx->device, &render_pass_info, nullptr, &framebuffer->render_pass); framebuffer->render_pass_id = ++seq; EXIT_NOT_IMPLEMENTED(framebuffer->render_pass == nullptr); VkImageView views[] = {vulkan_buffer->image_view, (with_depth ? depth->vulkan_buffer->image_view : nullptr)}; VkFramebufferCreateInfo framebuffer_info {}; framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebuffer_info.pNext = nullptr; framebuffer_info.flags = 0; framebuffer_info.renderPass = framebuffer->render_pass; framebuffer_info.attachmentCount = with_depth ? 2 : 1; framebuffer_info.pAttachments = views; framebuffer_info.width = vulkan_buffer->extent.width; framebuffer_info.height = vulkan_buffer->extent.height; framebuffer_info.layers = 1; vkCreateFramebuffer(gctx->device, &framebuffer_info, nullptr, &framebuffer->framebuffer); EXIT_NOT_IMPLEMENTED(framebuffer->framebuffer == nullptr); Framebuffer fnew; fnew.framebuffer = framebuffer; fnew.image_id = (with_color ? color->vulkan_buffer->memory.unique_id : 0); fnew.depth_id = (with_depth ? depth->vulkan_buffer->memory.unique_id : 0); fnew.depth_clear_enable = depth->depth_clear_enable; fnew.stencil_clear_enable = depth->stencil_clear_enable; bool updated = false; for (auto& f: m_framebuffers) { if (f.framebuffer == nullptr) { f = fnew; updated = true; break; } } if (!updated) { m_framebuffers.Add(fnew); } return framebuffer; } void FramebufferCache::FreeFramebufferByColor(VulkanImage* image) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(image == nullptr); Core::LockGuard lock(m_mutex); for (auto& f: m_framebuffers) { if (f.framebuffer != nullptr && f.image_id == image->memory.unique_id) { g_render_ctx->GetPipelineCache()->DeletePipelines(f.framebuffer); auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); vkDestroyFramebuffer(gctx->device, f.framebuffer->framebuffer, nullptr); vkDestroyRenderPass(gctx->device, f.framebuffer->render_pass, nullptr); delete f.framebuffer; f.framebuffer = nullptr; break; } } } void FramebufferCache::FreeFramebufferByDepth(DepthStencilVulkanImage* image) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(image == nullptr); Core::LockGuard lock(m_mutex); for (auto& f: m_framebuffers) { if (f.framebuffer != nullptr && f.depth_id == image->memory.unique_id) { g_render_ctx->GetPipelineCache()->DeletePipelines(f.framebuffer); auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); vkDestroyFramebuffer(gctx->device, f.framebuffer->framebuffer, nullptr); vkDestroyRenderPass(gctx->device, f.framebuffer->render_pass, nullptr); delete f.framebuffer; f.framebuffer = nullptr; break; } } } VideoOutVulkanImage* FramebufferCache::CreateDummyBuffer(VkFormat format, uint32_t width, uint32_t height) { for (auto* b: m_dummy_buffers) { if (b->extent.width == width && b->extent.height == height && b->format == format) { return b; } } auto* ctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(ctx == nullptr); auto* vk_obj = new VideoOutVulkanImage; vk_obj->extent.width = width; vk_obj->extent.height = height; vk_obj->format = format; vk_obj->image = nullptr; vk_obj->image_view = nullptr; vk_obj->layout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageCreateInfo image_info {}; image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = nullptr; image_info.flags = 0; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.extent.width = vk_obj->extent.width; image_info.extent.height = vk_obj->extent.height; image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; image_info.format = vk_obj->format; image_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_info.initialLayout = vk_obj->layout; image_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.samples = VK_SAMPLE_COUNT_1_BIT; vkCreateImage(ctx->device, &image_info, nullptr, &vk_obj->image); EXIT_NOT_IMPLEMENTED(vk_obj->image == nullptr); VulkanMemory mem; vkGetImageMemoryRequirements(ctx->device, vk_obj->image, &mem.requirements); mem.property = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; bool allocated = VulkanAllocate(ctx, &mem); EXIT_NOT_IMPLEMENTED(!allocated); VulkanBindImageMemory(ctx, vk_obj, &mem); vk_obj->memory = mem; VkImageViewCreateInfo create_info {}; create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; create_info.pNext = nullptr; create_info.flags = 0; create_info.image = vk_obj->image; create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; create_info.format = vk_obj->format; create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; create_info.subresourceRange.baseArrayLayer = 0; create_info.subresourceRange.baseMipLevel = 0; create_info.subresourceRange.layerCount = 1; create_info.subresourceRange.levelCount = 1; vkCreateImageView(ctx->device, &create_info, nullptr, &vk_obj->image_view); EXIT_NOT_IMPLEMENTED(vk_obj->image_view == nullptr); UtilSetImageLayoutOptimal(vk_obj); m_dummy_buffers.Add(vk_obj); return vk_obj; } VkSampler SamplerCache::GetSampler(uint64_t id) { Core::LockGuard lock(m_mutex); if (id < m_samplers.Size()) { return m_samplers.At(id).vk; } return nullptr; } uint64_t SamplerCache::GetSamplerId(const ShaderSamplerResource& r) { Core::LockGuard lock(m_mutex); uint32_t m_samplers_size = m_samplers.Size(); for (uint32_t i = 0; i < m_samplers_size; i++) { const auto& s = m_samplers.At(i); if (s.r.fields[0] == r.fields[0] && s.r.fields[1] == r.fields[1] && s.r.fields[2] == r.fields[2] && s.r.fields[3] == r.fields[3]) { return i; } } Sampler s; s.r = r; s.vk = nullptr; bool aniso = false; float aniso_ratio = 1.0f; auto mag_filter = r.XyMagFilter(); auto min_filter = r.XyMinFilter(); if (mag_filter == 2 || mag_filter == 3 || min_filter == 2 || min_filter == 3) { aniso = true; switch (r.MaxAnisoRatio()) { case 0: aniso_ratio = 1.0f; break; case 1: aniso_ratio = 2.0f; break; case 2: aniso_ratio = 4.0f; break; case 3: aniso_ratio = 8.0f; break; case 4: aniso_ratio = 16.0f; break; default: EXIT("unknown ratio: %d\n", static_cast(r.MaxAnisoRatio())); } } auto mip_filter = r.MipFilter(); float min_lod = 0.0f; float max_lod = 0.0f; if (mip_filter != 0) { min_lod = static_cast(r.MinLod()) / 256.0f; max_lod = static_cast(r.MaxLod()) / 256.0f; } VkSamplerCreateInfo sampler_info {}; auto get_warp = [](uint8_t clamp) { switch (clamp) { case 0: return VK_SAMPLER_ADDRESS_MODE_REPEAT; break; case 1: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; break; case 2: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; break; case 6: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; break; default: EXIT("unknown clamp: %u\n", clamp); } return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; }; VkBorderColor border = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK; switch (r.BorderColorType()) { case 0: border = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK; break; case 1: border = VK_BORDER_COLOR_INT_OPAQUE_BLACK; break; case 2: border = VK_BORDER_COLOR_INT_OPAQUE_WHITE; break; default: EXIT("unknown border color: %d", static_cast(r.BorderColorType())); } sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler_info.pNext = nullptr; sampler_info.flags = 0; sampler_info.magFilter = (mag_filter == 0 || mag_filter == 2 ? VK_FILTER_NEAREST : VK_FILTER_LINEAR); sampler_info.minFilter = (min_filter == 0 || min_filter == 2 ? VK_FILTER_NEAREST : VK_FILTER_LINEAR); sampler_info.mipmapMode = (mip_filter == 2 ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST); sampler_info.addressModeU = get_warp(r.ClampX()); sampler_info.addressModeV = get_warp(r.ClampY()); sampler_info.addressModeW = get_warp(r.ClampZ()); sampler_info.mipLodBias = static_cast(static_cast((r.LodBias() ^ 0x2000u) - 0x2000u)) / 256.0f; sampler_info.anisotropyEnable = (aniso ? VK_TRUE : VK_FALSE); sampler_info.maxAnisotropy = aniso_ratio; sampler_info.compareEnable = VK_TRUE; sampler_info.compareOp = static_cast(r.DepthCompareFunc()); sampler_info.minLod = min_lod; sampler_info.maxLod = max_lod; sampler_info.borderColor = border; sampler_info.unnormalizedCoordinates = (r.ForceUnormCoords() ? VK_TRUE : VK_FALSE); vkCreateSampler(g_render_ctx->GetGraphicCtx()->device, &sampler_info, nullptr, &s.vk); EXIT_NOT_IMPLEMENTED(s.vk == nullptr); m_samplers.Add(s); return m_samplers_size; } static VkFormat get_input_format(const ShaderBufferResource& res) { auto nfmt = res.Nfmt(); auto dfmt = res.Dfmt(); if (nfmt == 7 && dfmt == 14) { return VK_FORMAT_R32G32B32A32_SFLOAT; } if (nfmt == 7 && dfmt == 13) { return VK_FORMAT_R32G32B32_SFLOAT; } if (nfmt == 7 && dfmt == 11) { return VK_FORMAT_R32G32_SFLOAT; } if (nfmt == 0 && dfmt == 10) { return VK_FORMAT_R8G8B8A8_UNORM; } EXIT("unknown format: nfmt = %u, dfmt = %u\n", nfmt, dfmt); return VK_FORMAT_UNDEFINED; } static VkBlendFactor get_blend_factor(uint32_t factor) { switch (factor) { case /* Zero */ 0x00000000: return VK_BLEND_FACTOR_ZERO; case /* One */ 0x00000001: return VK_BLEND_FACTOR_ONE; case /* SrcColor */ 0x00000002: return VK_BLEND_FACTOR_SRC_COLOR; case /* OneMinusSrcColor */ 0x00000003: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; case /* SrcAlpha */ 0x00000004: return VK_BLEND_FACTOR_SRC_ALPHA; case /* OneMinusSrcAlpha */ 0x00000005: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; case /* DestAlpha */ 0x00000006: return VK_BLEND_FACTOR_DST_ALPHA; case /* OneMinusDestAlpha */ 0x00000007: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; case /* DestColor */ 0x00000008: return VK_BLEND_FACTOR_DST_COLOR; case /* OneMinusDestColor */ 0x00000009: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; case /* SrcAlphaSaturate */ 0x0000000a: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; case /* ConstantColor */ 0x0000000d: return VK_BLEND_FACTOR_CONSTANT_COLOR; case /* OneMinusConstantColor */ 0x0000000e: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; case /* Src1Color */ 0x0000000f: return VK_BLEND_FACTOR_SRC1_COLOR; case /* InverseSrc1Color */ 0x00000010: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR; case /* Src1Alpha */ 0x00000011: return VK_BLEND_FACTOR_SRC1_ALPHA; case /* InverseSrc1Alpha */ 0x00000012: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA; case /* ConstantAlpha */ 0x00000013: return VK_BLEND_FACTOR_CONSTANT_ALPHA; case /* OneMinusConstantAlpha */ 0x00000014: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA; default: EXIT("unknown factor: %u\n", factor); } return VK_BLEND_FACTOR_ZERO; } static VkBlendOp get_blend_op(uint32_t op) { switch (op) { case /* Add */ 0x00000000: return VK_BLEND_OP_ADD; case /* Subtract */ 0x00000001: return VK_BLEND_OP_SUBTRACT; case /* Min */ 0x00000002: return VK_BLEND_OP_MIN; case /* Max */ 0x00000003: return VK_BLEND_OP_MAX; case /* ReverseSubtract */ 0x00000004: return VK_BLEND_OP_REVERSE_SUBTRACT; default: EXIT("unknown op: %u\n", op); } return VK_BLEND_OP_ADD; } static void CreateLayout(VkDescriptorSetLayout* set_layouts, uint32_t* set_layouts_num, VkPushConstantRange* push_constant_info, uint32_t* push_constant_info_num, const ShaderBindResources& bind, const ShaderBindParameters& bind_params, VkShaderStageFlags vk_stage, DescriptorCache::Stage stage) { EXIT_IF(set_layouts == nullptr); EXIT_IF(set_layouts_num == nullptr); EXIT_IF(push_constant_info == nullptr); EXIT_IF(push_constant_info_num == nullptr); bool need_bind = (bind.storage_buffers.buffers_num > 0 || bind.textures2D.textures_num > 0 || bind.samplers.samplers_num > 0 || bind.gds_pointers.pointers_num > 0); EXIT_IF(need_bind && bind.push_constant_size == 0); if (need_bind) { auto index = *push_constant_info_num; push_constant_info[index].stageFlags = vk_stage; push_constant_info[index].offset = bind.push_constant_offset; push_constant_info[index].size = bind.push_constant_size; (*push_constant_info_num)++; } if (need_bind) { EXIT_IF(bind.descriptor_set_slot != *set_layouts_num); set_layouts[*set_layouts_num] = g_render_ctx->GetDescriptorCache()->GetDescriptorSetLayout(stage, bind, bind_params); (*set_layouts_num)++; } } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static VulkanPipeline* CreatePipelineInternal(VkRenderPass render_pass, const ShaderVertexInputInfo* vs_input_info, const Vector& vs_shader, const ShaderPixelInputInfo* ps_input_info, const Vector& ps_shader, const PipelineStaticParameters* static_params, PipelineDynamicParameters* dynamic_params, const PipelineAdditionalParameters* additional_params) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(render_pass == nullptr); EXIT_IF(static_params == nullptr); EXIT_IF(dynamic_params == nullptr); EXIT_IF(additional_params == nullptr); auto* pipeline = new VulkanPipeline; pipeline->additional_params = additional_params; pipeline->static_params = static_params; pipeline->dynamic_params = dynamic_params; auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); VkShaderModule vert_shader_module = nullptr; VkShaderModule frag_shader_module = nullptr; VkShaderModuleCreateInfo create_info {}; create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; create_info.pNext = nullptr; create_info.flags = 0; create_info.codeSize = vs_shader.Size() * 4; create_info.pCode = vs_shader.GetDataConst(); vkCreateShaderModule(gctx->device, &create_info, nullptr, &vert_shader_module); create_info.codeSize = ps_shader.Size() * 4; create_info.pCode = ps_shader.GetDataConst(); vkCreateShaderModule(gctx->device, &create_info, nullptr, &frag_shader_module); EXIT_NOT_IMPLEMENTED(vert_shader_module == nullptr); EXIT_NOT_IMPLEMENTED(frag_shader_module == nullptr); VkPipelineShaderStageCreateInfo vert_shader_stage_info {}; vert_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vert_shader_stage_info.pNext = nullptr; vert_shader_stage_info.flags = 0; vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; vert_shader_stage_info.module = vert_shader_module; vert_shader_stage_info.pName = "main"; vert_shader_stage_info.pSpecializationInfo = nullptr; VkPipelineShaderStageCreateInfo frag_shader_stage_info {}; frag_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; frag_shader_stage_info.pNext = nullptr; frag_shader_stage_info.flags = 0; frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; frag_shader_stage_info.module = frag_shader_module; frag_shader_stage_info.pName = "main"; frag_shader_stage_info.pSpecializationInfo = nullptr; VkPipelineShaderStageCreateInfo shader_stages[] = {vert_shader_stage_info, frag_shader_stage_info}; VkVertexInputAttributeDescription input_attr[ShaderVertexInputInfo::RES_MAX]; VkVertexInputBindingDescription input_desc[ShaderVertexInputInfo::RES_MAX]; for (int bi = 0; bi < vs_input_info->buffers_num; bi++) { const auto& b = vs_input_info->buffers[bi]; input_desc[bi].binding = bi; input_desc[bi].stride = b.stride; input_desc[bi].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; for (int ai = 0; ai < b.attr_num; ai++) { input_attr[ai].binding = bi; input_attr[ai].location = ai; input_attr[ai].offset = b.attr_offsets[ai]; input_attr[ai].format = get_input_format(vs_input_info->resources[ai]); auto registers_num = vs_input_info->resources_dst[ai].registers_num; EXIT_NOT_IMPLEMENTED(vs_input_info->resources[ai].AddTid()); EXIT_NOT_IMPLEMENTED(vs_input_info->resources[ai].SwizzleEnabled()); EXIT_NOT_IMPLEMENTED(vs_input_info->resources[ai].DstSelX() != 4); EXIT_NOT_IMPLEMENTED(registers_num >= 2 && vs_input_info->resources[ai].DstSelY() != 5); EXIT_NOT_IMPLEMENTED(registers_num >= 3 && vs_input_info->resources[ai].DstSelZ() != 6); EXIT_NOT_IMPLEMENTED(registers_num >= 4 && vs_input_info->resources[ai].DstSelW() != 7); } } VkPipelineVertexInputStateCreateInfo vertex_input_info {}; vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_info.pNext = nullptr; vertex_input_info.flags = 0; vertex_input_info.vertexBindingDescriptionCount = vs_input_info->buffers_num; vertex_input_info.pVertexBindingDescriptions = input_desc; vertex_input_info.vertexAttributeDescriptionCount = vs_input_info->resources_num; vertex_input_info.pVertexAttributeDescriptions = input_attr; VkPipelineInputAssemblyStateCreateInfo input_assembly {}; input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; input_assembly.pNext = nullptr; input_assembly.flags = 0; input_assembly.topology = static_params->topology; input_assembly.primitiveRestartEnable = VK_FALSE; VkViewport viewport {}; viewport.x = static_params->viewport_offset[0] - static_params->viewport_scale[0]; viewport.y = static_params->viewport_offset[1] - static_params->viewport_scale[1]; viewport.width = static_params->viewport_scale[0] * 2.0f; viewport.height = static_params->viewport_scale[1] * 2.0f; viewport.minDepth = static_params->viewport_offset[2]; viewport.maxDepth = static_params->viewport_scale[2] + static_params->viewport_offset[2]; VkRect2D scissor {}; scissor.offset = {static_params->scissor_ltrb[0], static_params->scissor_ltrb[1]}; scissor.extent = {static_cast(static_params->scissor_ltrb[2] - static_params->scissor_ltrb[0]), static_cast(static_params->scissor_ltrb[3] - static_params->scissor_ltrb[1])}; VkPipelineViewportStateCreateInfo viewport_state {}; viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state.pNext = nullptr; viewport_state.flags = 0; viewport_state.viewportCount = 1; viewport_state.pViewports = &viewport; viewport_state.scissorCount = 1; viewport_state.pScissors = &scissor; VkCullModeFlags cull_mode = VK_CULL_MODE_NONE; cull_mode |= (static_params->cull_back ? VK_CULL_MODE_BACK_BIT : 0u); cull_mode |= (static_params->cull_front ? VK_CULL_MODE_FRONT_BIT : 0u); VkFrontFace front_face = (static_params->face ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE); VkPipelineRasterizationDepthClipStateCreateInfoEXT clip_ext {}; clip_ext.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT; clip_ext.pNext = nullptr; clip_ext.flags = 0; clip_ext.depthClipEnable = VK_FALSE; VkPipelineRasterizationStateCreateInfo rasterizer {}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.pNext = &clip_ext; rasterizer.flags = 0; rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.cullMode = cull_mode; rasterizer.frontFace = front_face; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; rasterizer.depthBiasClamp = 0.0f; rasterizer.depthBiasSlopeFactor = 0.0f; rasterizer.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo multisampling {}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.pNext = nullptr; multisampling.flags = 0; multisampling.sampleShadingEnable = VK_FALSE; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampling.minSampleShading = 1.0f; multisampling.pSampleMask = nullptr; multisampling.alphaToCoverageEnable = VK_FALSE; multisampling.alphaToOneEnable = VK_FALSE; VkColorComponentFlags color_write_mask = 0; if (static_params->color_mask == 0xF) { color_write_mask = static_cast(VK_COLOR_COMPONENT_R_BIT) | static_cast(VK_COLOR_COMPONENT_G_BIT) | static_cast(VK_COLOR_COMPONENT_B_BIT) | static_cast(VK_COLOR_COMPONENT_A_BIT); } else if (static_params->color_mask == 0x0) { color_write_mask = 0; } else { EXIT("unknown mask: %u\n", static_params->color_mask); } VkPipelineColorBlendAttachmentState color_blend_attachment {}; color_blend_attachment.colorWriteMask = color_write_mask; color_blend_attachment.blendEnable = static_params->blend_enable ? VK_TRUE : VK_FALSE; color_blend_attachment.srcColorBlendFactor = get_blend_factor(static_params->color_srcblend); color_blend_attachment.dstColorBlendFactor = get_blend_factor(static_params->color_destblend); color_blend_attachment.colorBlendOp = get_blend_op(static_params->color_comb_fcn); color_blend_attachment.srcAlphaBlendFactor = (static_params->separate_alpha_blend ? get_blend_factor(static_params->alpha_srcblend) : color_blend_attachment.srcColorBlendFactor); color_blend_attachment.dstAlphaBlendFactor = (static_params->separate_alpha_blend ? get_blend_factor(static_params->alpha_destblend) : color_blend_attachment.dstColorBlendFactor); color_blend_attachment.alphaBlendOp = (static_params->separate_alpha_blend ? get_blend_op(static_params->alpha_comb_fcn) : color_blend_attachment.colorBlendOp); VkPipelineColorBlendStateCreateInfo color_blending {}; color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; color_blending.pNext = nullptr; color_blending.flags = 0; color_blending.logicOpEnable = VK_FALSE; color_blending.logicOp = VK_LOGIC_OP_COPY; color_blending.attachmentCount = 1; color_blending.pAttachments = &color_blend_attachment; color_blending.blendConstants[0] = static_params->blend_color_red; color_blending.blendConstants[1] = static_params->blend_color_green; color_blending.blendConstants[2] = static_params->blend_color_blue; color_blending.blendConstants[3] = static_params->blend_color_alpha; VkDescriptorSetLayout set_layouts[2] = {}; uint32_t set_layouts_num = 0; VkPushConstantRange push_constant_info[2]; uint32_t push_constant_info_num = 0; CreateLayout(set_layouts, &set_layouts_num, push_constant_info, &push_constant_info_num, vs_input_info->bind, additional_params->vs_bind, VK_SHADER_STAGE_VERTEX_BIT, DescriptorCache::Stage::Vertex); CreateLayout(set_layouts, &set_layouts_num, push_constant_info, &push_constant_info_num, ps_input_info->bind, additional_params->ps_bind, VK_SHADER_STAGE_FRAGMENT_BIT, DescriptorCache::Stage::Pixel); VkPipelineLayoutCreateInfo pipeline_layout_info {}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.pNext = nullptr; pipeline_layout_info.flags = 0; pipeline_layout_info.setLayoutCount = set_layouts_num; pipeline_layout_info.pSetLayouts = (set_layouts_num > 0 ? set_layouts : nullptr); pipeline_layout_info.pushConstantRangeCount = push_constant_info_num; pipeline_layout_info.pPushConstantRanges = push_constant_info_num > 0 ? push_constant_info : nullptr; EXIT_IF(pipeline->pipeline_layout != nullptr); vkCreatePipelineLayout(gctx->device, &pipeline_layout_info, nullptr, &pipeline->pipeline_layout); EXIT_NOT_IMPLEMENTED(pipeline->pipeline_layout == nullptr); VkPipelineDepthStencilStateCreateInfo depth_stencil_info {}; depth_stencil_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depth_stencil_info.pNext = nullptr; depth_stencil_info.flags = 0; depth_stencil_info.depthTestEnable = (static_params->depth_test_enable ? VK_TRUE : VK_FALSE); depth_stencil_info.depthWriteEnable = (static_params->depth_write_enable ? VK_TRUE : VK_FALSE); depth_stencil_info.depthCompareOp = static_params->depth_compare_op; depth_stencil_info.depthBoundsTestEnable = (static_params->depth_bounds_test_enable ? VK_TRUE : VK_FALSE); depth_stencil_info.stencilTestEnable = (static_params->stencil_test_enable ? VK_TRUE : VK_FALSE); depth_stencil_info.front.failOp = static_params->stencil_front.failOp; depth_stencil_info.front.passOp = static_params->stencil_front.passOp; depth_stencil_info.front.depthFailOp = static_params->stencil_front.depthFailOp; depth_stencil_info.front.compareOp = static_params->stencil_front.compareOp; depth_stencil_info.front.compareMask = dynamic_params->stencil_front.compareMask; depth_stencil_info.front.writeMask = dynamic_params->stencil_front.writeMask; depth_stencil_info.front.reference = dynamic_params->stencil_front.reference; depth_stencil_info.back.failOp = static_params->stencil_back.failOp; depth_stencil_info.back.passOp = static_params->stencil_back.passOp; depth_stencil_info.back.depthFailOp = static_params->stencil_back.depthFailOp; depth_stencil_info.back.compareOp = static_params->stencil_back.compareOp; depth_stencil_info.back.compareMask = dynamic_params->stencil_back.compareMask; depth_stencil_info.back.writeMask = dynamic_params->stencil_back.writeMask; depth_stencil_info.back.reference = dynamic_params->stencil_back.reference; depth_stencil_info.minDepthBounds = static_params->depth_min_bounds; depth_stencil_info.maxDepthBounds = static_params->depth_max_bounds; VkDynamicState dynamic_states[8] = {}; uint32_t dynamic_states_count = 0; if (dynamic_params->vk_dynamic_state_stencil_compare_mask) { dynamic_states[dynamic_states_count++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK; } if (dynamic_params->vk_dynamic_state_stencil_reference) { dynamic_states[dynamic_states_count++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; } if (dynamic_params->vk_dynamic_state_stencil_write_mask) { dynamic_states[dynamic_states_count++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK; } VkPipelineDynamicStateCreateInfo dynamic_state {}; dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state.pNext = nullptr; dynamic_state.flags = 0; dynamic_state.dynamicStateCount = dynamic_states_count; dynamic_state.pDynamicStates = dynamic_states; VkGraphicsPipelineCreateInfo pipeline_info {}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.pNext = nullptr; pipeline_info.flags = 0; pipeline_info.stageCount = 2; pipeline_info.pStages = shader_stages; pipeline_info.pVertexInputState = &vertex_input_info; pipeline_info.pInputAssemblyState = &input_assembly; pipeline_info.pTessellationState = nullptr; pipeline_info.pViewportState = &viewport_state; pipeline_info.pRasterizationState = &rasterizer; pipeline_info.pMultisampleState = &multisampling; pipeline_info.pDepthStencilState = (static_params->with_depth ? &depth_stencil_info : nullptr); pipeline_info.pColorBlendState = &color_blending; pipeline_info.pDynamicState = &dynamic_state; pipeline_info.layout = pipeline->pipeline_layout; pipeline_info.renderPass = render_pass; pipeline_info.subpass = 0; pipeline_info.basePipelineHandle = nullptr; pipeline_info.basePipelineIndex = -1; EXIT_IF(pipeline->pipeline != nullptr); vkCreateGraphicsPipelines(gctx->device, nullptr, 1, &pipeline_info, nullptr, &pipeline->pipeline); EXIT_NOT_IMPLEMENTED(pipeline->pipeline == nullptr); vkDestroyShaderModule(gctx->device, frag_shader_module, nullptr); vkDestroyShaderModule(gctx->device, vert_shader_module, nullptr); return pipeline; } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static VulkanPipeline* CreatePipelineInternal(const ShaderComputeInputInfo* input_info, const Vector& cs_shader, const PipelineStaticParameters* static_params, PipelineDynamicParameters* dynamic_params, const PipelineAdditionalParameters* additional_params) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(static_params == nullptr); EXIT_IF(dynamic_params == nullptr); EXIT_IF(additional_params == nullptr); auto* pipeline = new VulkanPipeline; pipeline->additional_params = additional_params; pipeline->static_params = static_params; pipeline->dynamic_params = dynamic_params; auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); VkShaderModule comp_shader_module = nullptr; VkShaderModuleCreateInfo create_info {}; create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; create_info.pNext = nullptr; create_info.flags = 0; create_info.codeSize = cs_shader.Size() * 4; create_info.pCode = cs_shader.GetDataConst(); vkCreateShaderModule(gctx->device, &create_info, nullptr, &comp_shader_module); EXIT_NOT_IMPLEMENTED(comp_shader_module == nullptr); VkPipelineShaderStageCreateInfo comp_shader_stage_info {}; comp_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; comp_shader_stage_info.pNext = nullptr; comp_shader_stage_info.flags = 0; comp_shader_stage_info.stage = VK_SHADER_STAGE_COMPUTE_BIT; comp_shader_stage_info.module = comp_shader_module; comp_shader_stage_info.pName = "main"; comp_shader_stage_info.pSpecializationInfo = nullptr; VkDescriptorSetLayout set_layouts[1] = {}; uint32_t set_layouts_num = 0; VkPushConstantRange push_constant_info[1]; uint32_t push_constant_info_num = 0; CreateLayout(set_layouts, &set_layouts_num, push_constant_info, &push_constant_info_num, input_info->bind, additional_params->cs_bind, VK_SHADER_STAGE_COMPUTE_BIT, DescriptorCache::Stage::Compute); VkPipelineLayoutCreateInfo pipeline_layout_info {}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.pNext = nullptr; pipeline_layout_info.flags = 0; pipeline_layout_info.setLayoutCount = set_layouts_num; pipeline_layout_info.pSetLayouts = (set_layouts_num > 0 ? set_layouts : nullptr); pipeline_layout_info.pushConstantRangeCount = push_constant_info_num; pipeline_layout_info.pPushConstantRanges = push_constant_info_num > 0 ? push_constant_info : nullptr; EXIT_IF(pipeline->pipeline_layout != nullptr); vkCreatePipelineLayout(gctx->device, &pipeline_layout_info, nullptr, &pipeline->pipeline_layout); EXIT_NOT_IMPLEMENTED(pipeline->pipeline_layout == nullptr); VkComputePipelineCreateInfo info {}; info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; info.stage = comp_shader_stage_info; info.layout = pipeline->pipeline_layout; info.basePipelineHandle = nullptr; info.basePipelineIndex = -1; EXIT_IF(pipeline->pipeline != nullptr); vkCreateComputePipelines(gctx->device, nullptr, 1, &info, nullptr, &pipeline->pipeline); EXIT_NOT_IMPLEMENTED(pipeline->pipeline == nullptr); vkDestroyShaderModule(gctx->device, comp_shader_module, nullptr); return pipeline; } void PipelineCache::DeletePipelineInternal(Pipeline& p) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(p.pipeline == nullptr); EXIT_IF(p.pipeline->pipeline == nullptr); EXIT_IF(p.pipeline->pipeline_layout == nullptr); EXIT_IF(p.static_params == nullptr); EXIT_IF(p.dynamic_params == nullptr); EXIT_IF(p.pipeline->additional_params == nullptr); EXIT_IF(p.pipeline->static_params != p.static_params); EXIT_IF(p.pipeline->dynamic_params != p.dynamic_params); delete p.static_params; delete p.dynamic_params; delete p.pipeline->additional_params; p.static_params = nullptr; p.dynamic_params = nullptr; p.pipeline->additional_params = nullptr; auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); vkDestroyPipeline(gctx->device, p.pipeline->pipeline, nullptr); vkDestroyPipelineLayout(gctx->device, p.pipeline->pipeline_layout, nullptr); delete p.pipeline; p.pipeline = nullptr; } bool PipelineStaticParameters::operator==(const PipelineStaticParameters& other) const { return (0 == memcmp(this, &other, sizeof(struct PipelineStaticParameters))); } bool PipelineDynamicParameters::operator==(const PipelineDynamicParameters& other) const { EXIT_IF(vk_dynamic_state_stencil_compare_mask != other.vk_dynamic_state_stencil_compare_mask || vk_dynamic_state_stencil_write_mask != other.vk_dynamic_state_stencil_write_mask || vk_dynamic_state_stencil_reference != other.vk_dynamic_state_stencil_reference); if (!vk_dynamic_state_stencil_compare_mask) { if (stencil_front.compareMask != other.stencil_front.compareMask || stencil_back.compareMask != other.stencil_back.compareMask) { return false; } } if (!vk_dynamic_state_stencil_write_mask) { if (stencil_front.writeMask != other.stencil_front.writeMask || stencil_back.writeMask != other.stencil_back.writeMask) { return false; } } if (!vk_dynamic_state_stencil_reference) { if (stencil_front.reference != other.stencil_front.reference || stencil_back.reference != other.stencil_back.reference) { return false; } } return true; } VulkanPipeline* PipelineCache::Find(const Pipeline& p) const { for (const auto& pn: m_pipelines) { if (pn.pipeline != nullptr && p.render_pass_id == pn.render_pass_id && p.vs_shader_id == pn.vs_shader_id && p.ps_shader_id == pn.ps_shader_id && p.cs_shader_id == pn.cs_shader_id && *p.static_params == *pn.static_params && *p.dynamic_params == *pn.dynamic_params) { return pn.pipeline; } } return nullptr; } VulkanPipeline* PipelineCache::CreatePipeline(VulkanFramebuffer* framebuffer, RenderColorInfo* color, RenderDepthInfo* depth, const ShaderVertexInputInfo* vs_input_info, const VertexShaderInfo* vs_regs, const ShaderPixelInputInfo* ps_input_info, const PixelShaderInfo* ps_regs, VkPrimitiveTopology topology, uint32_t color_mask, const ModeControl& mc, const BlendControl& bc, const BlendColor& bclr, const ScreenViewport& vp) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200); EXIT_IF(framebuffer == nullptr); EXIT_IF(vs_regs == nullptr); EXIT_IF(ps_regs == nullptr); EXIT_IF(depth == nullptr); EXIT_IF(color == nullptr); Core::LockGuard lock(m_mutex); if (Config::GetPrintfDirection() != Log::Direction::Silent) { ShaderDbgDumpInputInfo(vs_input_info); ShaderDbgDumpInputInfo(ps_input_info); } auto vs_id = ShaderGetIdVS(vs_regs, vs_input_info); auto ps_id = ShaderGetIdPS(ps_regs, ps_input_info); Pipeline p {}; p.render_pass_id = framebuffer->render_pass_id; p.ps_shader_id = ps_id; p.vs_shader_id = vs_id; p.static_params = new PipelineStaticParameters; p.dynamic_params = new PipelineDynamicParameters; p.dynamic_params->vk_dynamic_state_stencil_compare_mask = true; p.dynamic_params->vk_dynamic_state_stencil_reference = true; p.dynamic_params->vk_dynamic_state_stencil_write_mask = true; EXIT_NOT_IMPLEMENTED(depth->depth_test_enable && ps_input_info->ps_execute_on_noop); p.static_params->viewport_scale[0] = vp.viewports[0].xscale; p.static_params->viewport_scale[1] = vp.viewports[0].yscale; p.static_params->viewport_scale[2] = vp.viewports[0].zscale; p.static_params->viewport_offset[0] = vp.viewports[0].xoffset; p.static_params->viewport_offset[1] = vp.viewports[0].yoffset; p.static_params->viewport_offset[2] = vp.viewports[0].zoffset; p.static_params->scissor_ltrb[0] = vp.scissor_left; p.static_params->scissor_ltrb[1] = vp.scissor_top; p.static_params->scissor_ltrb[2] = vp.scissor_right; p.static_params->scissor_ltrb[3] = vp.scissor_bottom; p.static_params->topology = topology; p.static_params->with_depth = (depth->format != VK_FORMAT_UNDEFINED && depth->vulkan_buffer != nullptr); p.static_params->depth_test_enable = depth->depth_test_enable; p.static_params->depth_write_enable = (depth->depth_write_enable && !depth->depth_clear_enable); p.static_params->depth_compare_op = depth->depth_compare_op; p.static_params->depth_bounds_test_enable = depth->depth_bounds_test_enable; p.static_params->depth_min_bounds = depth->depth_min_bounds; p.static_params->depth_max_bounds = depth->depth_max_bounds; p.static_params->stencil_test_enable = depth->stencil_test_enable; p.static_params->stencil_front = depth->stencil_static_front; p.static_params->stencil_back = depth->stencil_static_back; p.static_params->color_mask = color_mask; p.static_params->cull_back = mc.cull_back; p.static_params->cull_front = mc.cull_front; p.static_params->face = mc.face; p.static_params->color_srcblend = bc.color_srcblend; p.static_params->color_comb_fcn = bc.color_comb_fcn; p.static_params->color_destblend = bc.color_destblend; p.static_params->alpha_srcblend = bc.alpha_srcblend; p.static_params->alpha_comb_fcn = bc.alpha_comb_fcn; p.static_params->alpha_destblend = bc.alpha_destblend; p.static_params->separate_alpha_blend = bc.separate_alpha_blend; p.static_params->blend_enable = bc.enable; p.static_params->blend_color_red = bclr.red; p.static_params->blend_color_green = bclr.green; p.static_params->blend_color_blue = bclr.blue; p.static_params->blend_color_alpha = bclr.alpha; p.dynamic_params->stencil_front = depth->stencil_dynamic_front; p.dynamic_params->stencil_back = depth->stencil_dynamic_back; auto* found = Find(p); if (found != nullptr) { *found->dynamic_params = *p.dynamic_params; delete p.static_params; delete p.dynamic_params; return found; } auto vs_code = ShaderParseVS(vs_regs); auto ps_code = ShaderParsePS(ps_regs); auto* params2 = new PipelineAdditionalParameters; params2->vs_bind = ShaderGetBindParametersVS(vs_code, vs_input_info); params2->ps_bind = ShaderGetBindParametersPS(ps_code, ps_input_info); auto vs_shader = ShaderRecompileVS(vs_code, vs_input_info); auto ps_shader = ShaderRecompilePS(ps_code, ps_input_info); EXIT_IF(vs_shader.IsEmpty()); EXIT_IF(ps_shader.IsEmpty()); p.pipeline = CreatePipelineInternal(framebuffer->render_pass, vs_input_info, vs_shader, ps_input_info, ps_shader, p.static_params, p.dynamic_params, params2); EXIT_NOT_IMPLEMENTED(p.pipeline == nullptr); bool updated = false; for (auto& pn: m_pipelines) { if (pn.pipeline == nullptr) { pn = p; updated = true; break; } } if (!updated) { if (m_pipelines.Size() >= PipelineCache::MAX_PIPELINES) { auto& pn = m_pipelines[Math::Rand::UintInclusiveRange(0, m_pipelines.Size() - 1)]; DeletePipelineInternal(pn); pn = p; } else { m_pipelines.Add(p); } } return p.pipeline; } VulkanPipeline* PipelineCache::CreatePipeline(const ShaderComputeInputInfo* input_info, const ComputeShaderInfo* cs_regs) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Compute)", profiler::colors::RedA100); EXIT_IF(cs_regs == nullptr); Core::LockGuard lock(m_mutex); ShaderDbgDumpInputInfo(input_info); auto cs_id = ShaderGetIdCS(cs_regs, input_info); Pipeline p {}; p.cs_shader_id = cs_id; p.static_params = new PipelineStaticParameters; p.dynamic_params = new PipelineDynamicParameters; p.dynamic_params->vk_dynamic_state_stencil_compare_mask = true; p.dynamic_params->vk_dynamic_state_stencil_reference = true; p.dynamic_params->vk_dynamic_state_stencil_write_mask = true; auto* found = Find(p); if (found != nullptr) { *found->dynamic_params = *p.dynamic_params; delete p.static_params; delete p.dynamic_params; return found; } auto cs_code = ShaderParseCS(cs_regs); auto* params2 = new PipelineAdditionalParameters; params2->cs_bind = ShaderGetBindParametersCS(cs_code, input_info); auto cs_shader = ShaderRecompileCS(cs_code, input_info); EXIT_IF(cs_shader.IsEmpty()); p.pipeline = CreatePipelineInternal(input_info, cs_shader, p.static_params, p.dynamic_params, params2); EXIT_NOT_IMPLEMENTED(p.pipeline == nullptr); bool updated = false; for (auto& pn: m_pipelines) { if (pn.pipeline == nullptr) { pn = p; updated = true; break; } } if (!updated) { if (m_pipelines.Size() >= PipelineCache::MAX_PIPELINES) { auto& pn = m_pipelines[Math::Rand::UintInclusiveRange(0, m_pipelines.Size() - 1)]; DeletePipelineInternal(pn); pn = p; } else { m_pipelines.Add(p); } } return p.pipeline; } void PipelineCache::DeletePipeline(VulkanPipeline* pipeline) { Core::LockGuard lock(m_mutex); auto index = m_pipelines.Find(pipeline, [](auto r, auto p) { return p == r.pipeline; }); EXIT_IF(!m_pipelines.IndexValid(index)); if (m_pipelines.IndexValid(index)) { DeletePipelineInternal(m_pipelines[index]); // m_pipelines.RemoveAt(index); m_pipelines[index].pipeline = nullptr; } } void PipelineCache::DeletePipelines(VulkanFramebuffer* framebuffer) { EXIT_IF(framebuffer == nullptr); Core::LockGuard lock(m_mutex); for (auto& p: m_pipelines) { if (p.pipeline != nullptr && p.render_pass_id == framebuffer->render_pass_id) { DeletePipelineInternal(p); p.pipeline = nullptr; } } } void PipelineCache::DeleteAllPipelines() { Core::LockGuard lock(m_mutex); for (auto& p: m_pipelines) { DeletePipelineInternal(p); p.pipeline = nullptr; } // m_pipelines.Clear(); } static void create_layout(GraphicContext* gctx, int storage_buffers_num, int textures2d_sampled_num, int textures2d_storage_num, int samplers_num, int gds_buffers_num, VkShaderStageFlags stage, VkDescriptorSetLayout* dst) { uint32_t binding_num = 0; ShaderBindResources tmp {}; tmp.storage_buffers.buffers_num = storage_buffers_num; tmp.textures2D.textures_num = textures2d_sampled_num + textures2d_storage_num; tmp.samplers.samplers_num = samplers_num; tmp.gds_pointers.pointers_num = gds_buffers_num; ShaderCalcBindingIndices(&tmp); constexpr uint32_t B_MAX = 5; VkDescriptorSetLayoutBinding ubo_layout_binding[B_MAX] = {}; if (storage_buffers_num > 0) { EXIT_IF(binding_num >= B_MAX); ubo_layout_binding[binding_num].binding = tmp.storage_buffers.binding_index; ubo_layout_binding[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; ubo_layout_binding[binding_num].descriptorCount = storage_buffers_num; ubo_layout_binding[binding_num].stageFlags = stage; ubo_layout_binding[binding_num].pImmutableSamplers = nullptr; binding_num++; } if (textures2d_sampled_num > 0) { EXIT_IF(binding_num >= B_MAX); ubo_layout_binding[binding_num].binding = tmp.textures2D.binding_sampled_index; ubo_layout_binding[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; ubo_layout_binding[binding_num].descriptorCount = textures2d_sampled_num; ubo_layout_binding[binding_num].stageFlags = stage; ubo_layout_binding[binding_num].pImmutableSamplers = nullptr; binding_num++; } if (textures2d_storage_num > 0) { EXIT_IF(binding_num >= B_MAX); ubo_layout_binding[binding_num].binding = tmp.textures2D.binding_storage_index; ubo_layout_binding[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; ubo_layout_binding[binding_num].descriptorCount = textures2d_storage_num; ubo_layout_binding[binding_num].stageFlags = stage; ubo_layout_binding[binding_num].pImmutableSamplers = nullptr; binding_num++; } if (samplers_num > 0) { EXIT_IF(binding_num >= B_MAX); ubo_layout_binding[binding_num].binding = tmp.samplers.binding_index; ubo_layout_binding[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; ubo_layout_binding[binding_num].descriptorCount = samplers_num; ubo_layout_binding[binding_num].stageFlags = stage; ubo_layout_binding[binding_num].pImmutableSamplers = nullptr; binding_num++; } if (gds_buffers_num > 0) { EXIT_IF(binding_num >= B_MAX); ubo_layout_binding[binding_num].binding = tmp.gds_pointers.binding_index; ubo_layout_binding[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; ubo_layout_binding[binding_num].descriptorCount = gds_buffers_num; ubo_layout_binding[binding_num].stageFlags = stage; ubo_layout_binding[binding_num].pImmutableSamplers = nullptr; binding_num++; } if (binding_num > 0) { VkDescriptorSetLayoutCreateInfo layout_info {}; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_info.pNext = nullptr; layout_info.flags = 0; layout_info.bindingCount = binding_num; layout_info.pBindings = ubo_layout_binding; EXIT_IF(*dst != nullptr); vkCreateDescriptorSetLayout(gctx->device, &layout_info, nullptr, dst); EXIT_NOT_IMPLEMENTED(*dst == nullptr); } else { *dst = nullptr; } }; void DescriptorCache::Init() { if (m_initialized) { return; } auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); EXIT_IF(m_descriptor_set_layout_vertex[0][0][0][0][0] != nullptr); EXIT_IF(m_descriptor_set_layout_pixel[0][0][0][0][0] != nullptr); EXIT_IF(m_descriptor_set_layout_compute[0][0][0][0][0] != nullptr); for (int buffers_num_i = 0; buffers_num_i <= BUFFERS_MAX; buffers_num_i++) { for (int buffers_num_j = 0; buffers_num_j <= TEXTURES_SAMPLED_MAX; buffers_num_j++) { for (int buffers_num_j2 = 0; buffers_num_j2 <= TEXTURES_STORAGE_MAX; buffers_num_j2++) { for (int buffers_num_k = 0; buffers_num_k <= SAMPLERS_MAX; buffers_num_k++) { for (int buffers_num_l = 0; buffers_num_l <= GDS_BUFFER_MAX; buffers_num_l++) { create_layout( gctx, buffers_num_i, buffers_num_j, buffers_num_j2, buffers_num_k, buffers_num_l, VK_SHADER_STAGE_FRAGMENT_BIT, &m_descriptor_set_layout_pixel[buffers_num_i][buffers_num_j][buffers_num_j2][buffers_num_k][buffers_num_l]); create_layout( gctx, buffers_num_i, buffers_num_j, buffers_num_j2, buffers_num_k, buffers_num_l, VK_SHADER_STAGE_VERTEX_BIT, &m_descriptor_set_layout_vertex[buffers_num_i][buffers_num_j][buffers_num_j2][buffers_num_k][buffers_num_l]); create_layout( gctx, buffers_num_i, buffers_num_j, buffers_num_j2, buffers_num_k, buffers_num_l, VK_SHADER_STAGE_COMPUTE_BIT, &m_descriptor_set_layout_compute[buffers_num_i][buffers_num_j][buffers_num_j2][buffers_num_k][buffers_num_l]); } } } } } m_initialized = true; } void DescriptorCache::CreatePool() { auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); VkDescriptorPoolSize pool_size[4]; pool_size[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; pool_size[0].descriptorCount = 32; pool_size[1].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; pool_size[1].descriptorCount = 32; pool_size[2].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; pool_size[2].descriptorCount = 32; pool_size[3].type = VK_DESCRIPTOR_TYPE_SAMPLER; pool_size[3].descriptorCount = 32; VkDescriptorPoolCreateInfo pool_info {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.pNext = nullptr; pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; pool_info.poolSizeCount = 4; pool_info.pPoolSizes = pool_size; pool_info.maxSets = 32; VkDescriptorPool pool = nullptr; vkCreateDescriptorPool(gctx->device, &pool_info, nullptr, &pool); EXIT_NOT_IMPLEMENTED(pool == nullptr); m_pools.Add(pool); } VulkanDescriptorSet* DescriptorCache::Allocate(Stage stage, int storage_buffers_num, int textures2d_sampled_num, int textures2d_storage_num, int samplers_num, int gds_buffers_num) { EXIT_IF(storage_buffers_num < 0 || storage_buffers_num > BUFFERS_MAX); EXIT_IF(textures2d_sampled_num < 0 || textures2d_sampled_num > TEXTURES_SAMPLED_MAX); EXIT_IF(textures2d_storage_num < 0 || textures2d_storage_num > TEXTURES_STORAGE_MAX); EXIT_IF(samplers_num < 0 || samplers_num > SAMPLERS_MAX); EXIT_IF(gds_buffers_num < 0 || gds_buffers_num > GDS_BUFFER_MAX); Core::LockGuard lock(m_mutex); auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); Init(); auto* ret = new VulkanDescriptorSet; ret->set = nullptr; for (int try_num = 0; try_num < 2; try_num++) { for (auto* pool: m_pools) { VkDescriptorSetAllocateInfo alloc_info {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.pNext = nullptr; alloc_info.descriptorPool = pool; alloc_info.descriptorSetCount = 1; switch (stage) { case Stage::Vertex: alloc_info.pSetLayouts = &m_descriptor_set_layout_vertex[storage_buffers_num][textures2d_sampled_num] [textures2d_storage_num][samplers_num][gds_buffers_num]; break; case Stage::Pixel: alloc_info.pSetLayouts = &m_descriptor_set_layout_pixel[storage_buffers_num][textures2d_sampled_num] [textures2d_storage_num][samplers_num][gds_buffers_num]; break; case Stage::Compute: alloc_info.pSetLayouts = &m_descriptor_set_layout_compute[storage_buffers_num][textures2d_sampled_num] [textures2d_storage_num][samplers_num][gds_buffers_num]; break; default: EXIT("unknown stage\n"); } ret->pool = pool; ret->layout = *alloc_info.pSetLayouts; auto result = vkAllocateDescriptorSets(gctx->device, &alloc_info, &ret->set); if (result == VK_SUCCESS) { return ret; } } CreatePool(); } delete ret; return nullptr; } void DescriptorCache::Free(VulkanDescriptorSet* set) { EXIT_IF(set == nullptr); Core::LockGuard lock(m_mutex); auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); vkFreeDescriptorSets(gctx->device, set->pool, 1, &set->set); delete set; } // NOLINTNEXTLINE(readability-function-cognitive-complexity) VulkanDescriptorSet* DescriptorCache::GetDescriptor(Stage stage, VulkanBuffer** storage_buffers, VulkanImage** textures2d_sampled, VulkanImage** textures2d_storage, uint64_t* samplers, VulkanBuffer** gds_buffers, const ShaderBindResources& bind, const ShaderBindParameters& bind_params) { KYTY_PROFILER_BLOCK("DescriptorCache::GetDescriptor"); int storage_buffers_num = bind.storage_buffers.buffers_num; int textures2d_sampled_num = bind_params.textures2d_sampled_num; int textures2d_storage_num = bind_params.textures2d_storage_num; int samplers_num = bind.samplers.samplers_num; int gds_buffers_num = bind.gds_pointers.pointers_num; EXIT_IF(storage_buffers_num < 0 || storage_buffers_num > BUFFERS_MAX); EXIT_IF(textures2d_sampled_num < 0 || textures2d_sampled_num > TEXTURES_SAMPLED_MAX); EXIT_IF(textures2d_storage_num < 0 || textures2d_storage_num > TEXTURES_STORAGE_MAX); EXIT_IF(samplers_num < 0 || samplers_num > SAMPLERS_MAX); EXIT_IF(storage_buffers == nullptr); Core::LockGuard lock(m_mutex); auto* gctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(gctx == nullptr); for (auto& set: m_sets) { if (set.set != nullptr && set.stage == stage && set.storage_buffers_num == storage_buffers_num && set.textures2d_sampled_num == textures2d_sampled_num && set.textures2d_storage_num == textures2d_storage_num && set.samplers_num == samplers_num && set.gds_buffers_num == gds_buffers_num) { bool match = true; for (int i = 0; i < storage_buffers_num; i++) { if (match && storage_buffers[i]->memory.unique_id != set.storage_buffers_id[i]) { match = false; break; } } if (match) { for (int i = 0; i < textures2d_sampled_num; i++) { if (textures2d_sampled[i]->memory.unique_id != set.textures2d_sampled_id[i]) { match = false; break; } } } if (match) { for (int i = 0; i < textures2d_storage_num; i++) { if (textures2d_storage[i]->memory.unique_id != set.textures2d_storage_id[i]) { match = false; break; } } } if (match) { for (int i = 0; i < samplers_num; i++) { if (samplers[i] != set.samplers_id[i]) { match = false; break; } } } if (match) { for (int i = 0; i < gds_buffers_num; i++) { if (gds_buffers[i]->memory.unique_id != set.gds_buffers_id[i]) { match = false; break; } } } if (match) { return set.set; } } } auto* new_set = Allocate(stage, storage_buffers_num, textures2d_sampled_num, textures2d_storage_num, samplers_num, gds_buffers_num); EXIT_NOT_IMPLEMENTED(new_set == nullptr); VkDescriptorBufferInfo buffer_info[BUFFERS_MAX] {}; for (int i = 0; i < storage_buffers_num; i++) { buffer_info[i].buffer = storage_buffers[i]->buffer; buffer_info[i].offset = 0; buffer_info[i].range = VK_WHOLE_SIZE; } VkDescriptorImageInfo texture2d_sampled_info[TEXTURES_SAMPLED_MAX] {}; for (int i = 0; i < textures2d_sampled_num; i++) { texture2d_sampled_info[i].sampler = nullptr; texture2d_sampled_info[i].imageView = textures2d_sampled[i]->image_view; texture2d_sampled_info[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } VkDescriptorImageInfo texture2d_storage_info[TEXTURES_STORAGE_MAX] {}; for (int i = 0; i < textures2d_storage_num; i++) { texture2d_storage_info[i].sampler = nullptr; texture2d_storage_info[i].imageView = textures2d_storage[i]->image_view; texture2d_storage_info[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; } VkDescriptorImageInfo sampler_info[SAMPLERS_MAX] {}; for (int i = 0; i < samplers_num; i++) { sampler_info[i].sampler = g_render_ctx->GetSamplerCache()->GetSampler(samplers[i]); sampler_info[i].imageView = nullptr; sampler_info[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } VkDescriptorBufferInfo gds_buffer_info[GDS_BUFFER_MAX] {}; for (int i = 0; i < gds_buffers_num; i++) { gds_buffer_info[i].buffer = gds_buffers[i]->buffer; gds_buffer_info[i].offset = 0; gds_buffer_info[i].range = VK_WHOLE_SIZE; } int binding_num = 0; constexpr uint32_t B_MAX = 5; VkWriteDescriptorSet descriptor_write[B_MAX] = {}; if (storage_buffers_num > 0) { EXIT_IF(binding_num >= B_MAX); descriptor_write[binding_num].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptor_write[binding_num].pNext = nullptr; descriptor_write[binding_num].dstSet = new_set->set; descriptor_write[binding_num].dstBinding = bind.storage_buffers.binding_index; descriptor_write[binding_num].dstArrayElement = 0; descriptor_write[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; descriptor_write[binding_num].descriptorCount = storage_buffers_num; descriptor_write[binding_num].pBufferInfo = buffer_info; descriptor_write[binding_num].pImageInfo = nullptr; descriptor_write[binding_num].pTexelBufferView = nullptr; binding_num++; } if (textures2d_sampled_num > 0) { EXIT_IF(binding_num >= B_MAX); descriptor_write[binding_num].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptor_write[binding_num].pNext = nullptr; descriptor_write[binding_num].dstSet = new_set->set; descriptor_write[binding_num].dstBinding = bind.textures2D.binding_sampled_index; descriptor_write[binding_num].dstArrayElement = 0; descriptor_write[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; descriptor_write[binding_num].descriptorCount = textures2d_sampled_num; descriptor_write[binding_num].pBufferInfo = nullptr; descriptor_write[binding_num].pImageInfo = texture2d_sampled_info; descriptor_write[binding_num].pTexelBufferView = nullptr; binding_num++; } if (textures2d_storage_num > 0) { EXIT_IF(binding_num >= B_MAX); descriptor_write[binding_num].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptor_write[binding_num].pNext = nullptr; descriptor_write[binding_num].dstSet = new_set->set; descriptor_write[binding_num].dstBinding = bind.textures2D.binding_storage_index; descriptor_write[binding_num].dstArrayElement = 0; descriptor_write[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; descriptor_write[binding_num].descriptorCount = textures2d_storage_num; descriptor_write[binding_num].pBufferInfo = nullptr; descriptor_write[binding_num].pImageInfo = texture2d_storage_info; descriptor_write[binding_num].pTexelBufferView = nullptr; binding_num++; } if (samplers_num > 0) { EXIT_IF(binding_num >= B_MAX); descriptor_write[binding_num].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptor_write[binding_num].pNext = nullptr; descriptor_write[binding_num].dstSet = new_set->set; descriptor_write[binding_num].dstBinding = bind.samplers.binding_index; descriptor_write[binding_num].dstArrayElement = 0; descriptor_write[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; descriptor_write[binding_num].descriptorCount = samplers_num; descriptor_write[binding_num].pBufferInfo = nullptr; descriptor_write[binding_num].pImageInfo = sampler_info; descriptor_write[binding_num].pTexelBufferView = nullptr; binding_num++; } if (gds_buffers_num > 0) { EXIT_IF(binding_num >= B_MAX); descriptor_write[binding_num].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptor_write[binding_num].pNext = nullptr; descriptor_write[binding_num].dstSet = new_set->set; descriptor_write[binding_num].dstBinding = bind.gds_pointers.binding_index; descriptor_write[binding_num].dstArrayElement = 0; descriptor_write[binding_num].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; descriptor_write[binding_num].descriptorCount = gds_buffers_num; descriptor_write[binding_num].pBufferInfo = gds_buffer_info; descriptor_write[binding_num].pImageInfo = nullptr; descriptor_write[binding_num].pTexelBufferView = nullptr; binding_num++; } vkUpdateDescriptorSets(gctx->device, binding_num, descriptor_write, 0, nullptr); Set nset; nset.set = new_set; nset.storage_buffers_num = storage_buffers_num; nset.textures2d_sampled_num = textures2d_sampled_num; nset.textures2d_storage_num = textures2d_storage_num; nset.samplers_num = samplers_num; nset.gds_buffers_num = gds_buffers_num; nset.stage = stage; for (int i = 0; i < storage_buffers_num; i++) { nset.storage_buffers_id[i] = storage_buffers[i]->memory.unique_id; } for (int i = 0; i < textures2d_sampled_num; i++) { nset.textures2d_sampled_id[i] = textures2d_sampled[i]->memory.unique_id; } for (int i = 0; i < textures2d_storage_num; i++) { nset.textures2d_storage_id[i] = textures2d_storage[i]->memory.unique_id; } for (int i = 0; i < samplers_num; i++) { nset.samplers_id[i] = samplers[i]; } for (int i = 0; i < gds_buffers_num; i++) { nset.gds_buffers_id[i] = gds_buffers[i]->memory.unique_id; } for (auto& set: m_sets) { if (set.set == nullptr) { set = nset; return new_set; } } m_sets.Add(nset); return new_set; } void DescriptorCache::FreeDescriptor(VulkanBuffer* buffer) { EXIT_IF(buffer == nullptr); Core::LockGuard lock(m_mutex); for (auto& set: m_sets) { if (set.set != nullptr) { for (int i = 0; i < set.storage_buffers_num; i++) { if (set.storage_buffers_id[i] == buffer->memory.unique_id) { Free(set.set); set.set = nullptr; break; } } } } } void DescriptorCache::FreeDescriptor(VulkanImage* image) { EXIT_IF(image == nullptr); Core::LockGuard lock(m_mutex); for (auto& set: m_sets) { if (set.set != nullptr) { for (int i = 0; i < set.textures2d_sampled_num; i++) { if (set.textures2d_sampled_id[i] == image->memory.unique_id) { Free(set.set); set.set = nullptr; break; } } } } } VkDescriptorSetLayout DescriptorCache::GetDescriptorSetLayout(Stage stage, const ShaderBindResources& bind, const ShaderBindParameters& bind_params) { int storage_buffers_num = bind.storage_buffers.buffers_num; int textures2d_sampled_num = bind_params.textures2d_sampled_num; int textures2d_storage_num = bind_params.textures2d_storage_num; int samplers_num = bind.samplers.samplers_num; int gds_buffers_num = bind.gds_pointers.pointers_num; EXIT_IF(storage_buffers_num < 0 || storage_buffers_num > BUFFERS_MAX); EXIT_IF(textures2d_sampled_num < 0 || textures2d_sampled_num > TEXTURES_SAMPLED_MAX); EXIT_IF(textures2d_storage_num < 0 || textures2d_storage_num > TEXTURES_STORAGE_MAX); EXIT_IF(samplers_num < 0 || samplers_num > SAMPLERS_MAX); EXIT_IF(gds_buffers_num < 0 || gds_buffers_num > GDS_BUFFER_MAX); EXIT_NOT_IMPLEMENTED(stage != Stage::Pixel && stage != Stage::Compute && (textures2d_sampled_num > 0 || textures2d_storage_num > 0 || samplers_num > 0)); Core::LockGuard lock(m_mutex); Init(); switch (stage) { case Stage::Vertex: return m_descriptor_set_layout_vertex[storage_buffers_num][textures2d_sampled_num][textures2d_storage_num][samplers_num] [gds_buffers_num]; case Stage::Pixel: return m_descriptor_set_layout_pixel[storage_buffers_num][textures2d_sampled_num][textures2d_storage_num][samplers_num] [gds_buffers_num]; case Stage::Compute: return m_descriptor_set_layout_compute[storage_buffers_num][textures2d_sampled_num][textures2d_storage_num][samplers_num] [gds_buffers_num]; default: EXIT("unknown stage\n"); } return nullptr; } void DeleteFramebuffer(VideoOutVulkanImage* image) { g_render_ctx->GetFramebufferCache()->FreeFramebufferByColor(image); } void DeleteFramebuffer(RenderTextureVulkanImage* image) { g_render_ctx->GetFramebufferCache()->FreeFramebufferByColor(image); } void DeleteFramebuffer(DepthStencilVulkanImage* image) { g_render_ctx->GetFramebufferCache()->FreeFramebufferByDepth(image); } void DeleteDescriptor(VulkanBuffer* buffer) { g_render_ctx->GetDescriptorCache()->FreeDescriptor(buffer); } void DeleteDescriptor(TextureVulkanImage* image) { g_render_ctx->GetDescriptorCache()->FreeDescriptor(image); } void DeleteDescriptor(StorageTextureVulkanImage* image) { g_render_ctx->GetDescriptorCache()->FreeDescriptor(image); } void DeleteDescriptor(RenderTextureVulkanImage* image) { g_render_ctx->GetDescriptorCache()->FreeDescriptor(image); } static bool get_stencil_op(VkStencilOp* f, uint32_t* ref, uint8_t op, uint8_t testval, uint8_t opval) { VkStencilOp vk = VK_STENCIL_OP_KEEP; uint32_t r = opval; bool use_ref = true; switch (op) { case 0x00: vk = VK_STENCIL_OP_KEEP; use_ref = false; break; /* Keep */ case 0x01: vk = VK_STENCIL_OP_ZERO; use_ref = false; break; /* Zero */ case 0x02: vk = VK_STENCIL_OP_REPLACE; r = 0xFF; break; /* Ones */ case 0x03: vk = VK_STENCIL_OP_REPLACE; r = testval; break; /* ReplaceTest */ case 0x04: vk = VK_STENCIL_OP_REPLACE; break; /* ReplaceOp */ case 0x05: vk = VK_STENCIL_OP_INCREMENT_AND_CLAMP; break; /* AddClamp */ case 0x06: vk = VK_STENCIL_OP_DECREMENT_AND_CLAMP; break; /* SubClamp */ case 0x07: vk = VK_STENCIL_OP_INVERT; break; /* Invert */ case 0x08: vk = VK_STENCIL_OP_INCREMENT_AND_WRAP; break; /* AddWrap */ case 0x09: vk = VK_STENCIL_OP_DECREMENT_AND_WRAP; break; /* SubWrap */ case 0x0a: /* And */ case 0x0b: /* Or */ case 0x0c: /* Xor */ case 0x0d: /* Nand */ case 0x0e: /* Nor */ case 0x0f: /* Xnor */ default: EXIT("invalid op"); } *f = vk; *ref = r; return use_ref; } static void get_stencil_state(PipelineStencilStaticState* s, PipelineStencilDynamicState* d, uint8_t func, uint8_t fail, uint8_t zpass, uint8_t zfail, uint8_t testval, uint8_t mask, uint8_t writemask, uint8_t opval) { EXIT_IF(s == nullptr || d == nullptr); uint32_t ref[3] = {}; bool use_ref[3] = {}; use_ref[0] = get_stencil_op(&s->failOp, ref + 0, fail, testval, opval); use_ref[1] = get_stencil_op(&s->passOp, ref + 1, zpass, testval, opval); use_ref[2] = get_stencil_op(&s->depthFailOp, ref + 2, zfail, testval, opval); s->compareOp = static_cast(func); d->compareMask = mask; d->writeMask = writemask; if (use_ref[0]) { EXIT_NOT_IMPLEMENTED((ref[0] != ref[1] && use_ref[1]) || (ref[0] != ref[2] && use_ref[2])); d->reference = ref[0]; } else if (use_ref[1]) { EXIT_NOT_IMPLEMENTED(ref[1] != ref[2] && use_ref[2]); d->reference = ref[1]; } else if (use_ref[2]) { d->reference = ref[2]; } else { d->reference = 0; } } static void FindRenderDepthInfo(CommandBuffer* /*buffer*/, const HardwareContext& hw, RenderDepthInfo* r) { KYTY_PROFILER_FUNCTION(); EXIT_IF(r == nullptr); bool neo = Config::IsNeo(); const auto& z = hw.GetDepthRenderTarget(); const auto& rc = hw.GetRenderControl(); const auto& dc = hw.GetDepthControl(); const auto& sc = hw.GetStencilControl(); const auto& sm = hw.GetStencilMask(); uint32_t size = 0; uint32_t stencil_size = 0; uint32_t htile_size = 0; uint32_t depth_size = 0; uint32_t pitch = 0; if (z.z_info.format == 3) { size = (z.slice_div64_minus1 + 1) * 64 * 4; } else if (z.z_info.format == 1) { size = (z.slice_div64_minus1 + 1) * 64 * 2; } bool htile = z.z_info.tile_surface_enable; TileGetDepthSize(z.width, z.height, z.z_info.format, z.stencil_info.format, htile, neo, &stencil_size, &htile_size, &depth_size, &pitch); if (pitch == 0) { depth_size = size; } else { EXIT_NOT_IMPLEMENTED(depth_size != size); EXIT_NOT_IMPLEMENTED((z.pitch_div8_minus1 + 1) * 8 != pitch); } switch (z.z_info.format * 2 + z.stencil_info.format) { case 0: r->format = VK_FORMAT_UNDEFINED; break; case 1: /*r->format = VK_FORMAT_S8_UINT*/ EXIT("Unsupported format: VK_FORMAT_S8_UINT\n"); break; case 2: r->format = VK_FORMAT_D16_UNORM; break; case 3: r->format = VK_FORMAT_D24_UNORM_S8_UINT; break; case 6: r->format = VK_FORMAT_D32_SFLOAT; break; case 7: r->format = VK_FORMAT_D32_SFLOAT_S8_UINT; break; default: EXIT("unknown z and stencil format: %u, %u\n", z.z_info.format, z.stencil_info.format); } r->htile = htile; r->neo = neo; r->depth_buffer_size = depth_size; r->depth_buffer_vaddr = z.z_read_base_addr; r->stencil_buffer_size = stencil_size; r->stencil_buffer_vaddr = z.stencil_read_base_addr; r->htile_buffer_size = htile_size; r->htile_buffer_vaddr = z.htile_data_base_addr; r->width = z.width; r->height = z.height; r->depth_clear_enable = rc.depth_clear_enable; r->depth_clear_value = hw.GetDepthClearValue(); r->depth_test_enable = dc.z_enable; r->depth_write_enable = dc.z_write_enable; r->depth_compare_op = static_cast(dc.zfunc); r->depth_bounds_test_enable = dc.depth_bounds_enable; r->depth_min_bounds = 0.0f; r->depth_max_bounds = 0.0f; EXIT_NOT_IMPLEMENTED(r->depth_bounds_test_enable); r->stencil_clear_enable = rc.stencil_clear_enable; r->stencil_clear_value = hw.GetStencilClearValue(); // EXIT_NOT_IMPLEMENTED(r->stencil_clear_enable); r->stencil_test_enable = dc.stencil_enable; if (r->stencil_test_enable) { get_stencil_state(&r->stencil_static_front, &r->stencil_dynamic_front, dc.stencilfunc, sc.stencil_fail, sc.stencil_zpass, sc.stencil_zfail, sm.stencil_testval, sm.stencil_mask, sm.stencil_writemask, sm.stencil_opval); if (dc.backface_enable) { get_stencil_state(&r->stencil_static_back, &r->stencil_dynamic_back, dc.stencilfunc_bf, sc.stencil_fail_bf, sc.stencil_zpass_bf, sc.stencil_zfail_bf, sm.stencil_testval_bf, sm.stencil_mask_bf, sm.stencil_writemask_bf, sm.stencil_opval_bf); } else { r->stencil_static_back = r->stencil_static_front; r->stencil_dynamic_back = r->stencil_dynamic_front; } } else { r->stencil_static_front = {}; r->stencil_static_back = {}; r->stencil_dynamic_front = {}; r->stencil_dynamic_back = {}; } // EXIT_NOT_IMPLEMENTED(r->stencil_test_enable); EXIT_NOT_IMPLEMENTED(dc.backface_enable); r->vulkan_buffer = nullptr; if (r->format != VK_FORMAT_UNDEFINED) { DepthStencilBufferObject vulkan_buffer_info(r->format, r->width, r->height, htile, neo); r->vaddr_num = 0; if (r->depth_buffer_size > 0) { r->vaddr[r->vaddr_num] = r->depth_buffer_vaddr; r->size[r->vaddr_num] = r->depth_buffer_size; r->vaddr_num++; } if (r->stencil_buffer_size > 0) { r->vaddr[r->vaddr_num] = r->stencil_buffer_vaddr; r->size[r->vaddr_num] = r->stencil_buffer_size; r->vaddr_num++; } if (r->htile_buffer_size > 0) { r->vaddr[r->vaddr_num] = r->htile_buffer_vaddr; r->size[r->vaddr_num] = r->htile_buffer_size; r->vaddr_num++; } EXIT_NOT_IMPLEMENTED(r->vaddr_num == 0); r->vulkan_buffer = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, r->vaddr, r->size, r->vaddr_num, vulkan_buffer_info)); EXIT_NOT_IMPLEMENTED(r->vulkan_buffer == nullptr); } } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static void FindRenderColorInfo(CommandBuffer* buffer, const HardwareContext& hw, RenderColorInfo* r) { KYTY_PROFILER_FUNCTION(); EXIT_IF(r == nullptr); const auto& rt = hw.GetRenderTargets(0); auto mask = hw.GetRenderTargetMask(); if (rt.base_addr == 0 || mask == 0) { // No color output r->type = RenderColorType::NoColorOutput; r->base_addr = 0; r->vulkan_buffer = nullptr; r->buffer_size = 0; return; } auto width = rt.width; auto height = rt.height; auto pitch = (rt.pitch_div8_minus1 + 1) * 8; auto size = (rt.slice_div64_minus1 + 1) * 64 * 4; bool tile = false; uint32_t format = 0; bool render_to_texture = false; if (rt.tile_mode == 0x8) { tile = false; } else if (rt.tile_mode == 0xa) { tile = true; } else if (rt.tile_mode == 0xd || rt.tile_mode == 0xe) { tile = true; render_to_texture = true; } else { EXIT("unknown tile mode: %u\n", rt.tile_mode); } if (render_to_texture) { if (rt.color_info.format == 0xa && rt.color_info.channel_type == 0x0 && rt.color_info.channel_order == 0x0) { // Render to texture RenderTextureObject vulkan_buffer_info(RenderTextureFormat::R8G8B8A8Unorm, width, height, tile, rt.color_info.neo_mode, pitch); auto* buffer_vulkan = static_cast( Graphics::GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), buffer, rt.base_addr, size, vulkan_buffer_info)); EXIT_NOT_IMPLEMENTED(buffer_vulkan == nullptr); r->type = RenderColorType::RenderTexture; r->base_addr = rt.base_addr; r->vulkan_buffer = buffer_vulkan; r->buffer_size = size; } else { EXIT("unknown format"); } } else { if (rt.color_info.format == 0xa && rt.color_info.channel_type == 0x6 && rt.color_info.channel_order == 0x1) { format = 0x80000000; } else { EXIT("unknown format"); } auto video_image = VideoOut::VideoOutGetImage(rt.base_addr); if (video_image.image == nullptr) { // Offscreen buffer VideoOutBufferObject vulkan_buffer_info(format, width, height, tile, Config::IsNeo(), pitch); auto* buffer_vulkan = static_cast( Graphics::GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, rt.base_addr, size, vulkan_buffer_info)); EXIT_NOT_IMPLEMENTED(buffer_vulkan == nullptr); r->type = RenderColorType::OffscreenBuffer; r->base_addr = rt.base_addr; r->vulkan_buffer = buffer_vulkan; r->buffer_size = size; } else { // Display buffer EXIT_NOT_IMPLEMENTED(video_image.buffer_size != size); EXIT_NOT_IMPLEMENTED(video_image.buffer_pitch != pitch); r->type = RenderColorType::DisplayBuffer; r->base_addr = rt.base_addr; r->vulkan_buffer = video_image.image; r->buffer_size = video_image.buffer_size; } } } static void InvalidateMemoryObject(const RenderColorInfo& r) { bool with_color = (r.vulkan_buffer != nullptr); if (with_color) { if (r.type == RenderColorType::RenderTexture) { GpuMemoryResetHash(g_render_ctx->GetGraphicCtx(), &r.base_addr, &r.buffer_size, 1, GpuMemoryObjectType::RenderTexture); } else if (r.type == RenderColorType::DisplayBuffer || r.type == RenderColorType::OffscreenBuffer) { GpuMemoryResetHash(g_render_ctx->GetGraphicCtx(), &r.base_addr, &r.buffer_size, 1, GpuMemoryObjectType::VideoOutBuffer); } } } static void InvalidateMemoryObject(const RenderDepthInfo& r) { bool with_depth = (r.format != VK_FORMAT_UNDEFINED && r.vulkan_buffer != nullptr); if (with_depth) { GpuMemoryResetHash(g_render_ctx->GetGraphicCtx(), r.vaddr, r.size, r.vaddr_num, GpuMemoryObjectType::DepthStencilBuffer); } } static Vector FindRenderTexture(uint64_t vaddr, uint64_t size, bool exact) { KYTY_PROFILER_FUNCTION(); Vector ret; auto objects = GpuMemoryFindObjects(vaddr, size, exact); for (const auto& obj: objects) { if (obj.type == GpuMemoryObjectType::RenderTexture) { ret.Add(static_cast(obj.obj)); } } return ret; } static void PrepareStorageBuffers(CommandBuffer* buffer, const ShaderStorageResources& storage_buffers, VulkanBuffer** buffers, uint32_t** sgprs) { KYTY_PROFILER_FUNCTION(); EXIT_IF(buffers == nullptr); EXIT_IF(sgprs == nullptr); EXIT_IF(*sgprs == nullptr); for (int i = 0; i < storage_buffers.buffers_num; i++) { auto r = storage_buffers.buffers[i]; EXIT_NOT_IMPLEMENTED(r.AddTid()); EXIT_NOT_IMPLEMENTED(r.SwizzleEnabled()); EXIT_NOT_IMPLEMENTED(!((r.Stride() == 4 && r.DstSelX() == 4 && r.DstSelY() == 0 && r.DstSelZ() == 0 && r.DstSelW() == 0 && r.Dfmt() == 4 && r.Nfmt() == 4) || (r.Stride() == 4 && r.DstSelX() == 4 && r.DstSelY() == 0 && r.DstSelZ() == 0 && r.DstSelW() == 1 && r.Dfmt() == 4 && r.Nfmt() == 7) || (r.Stride() == 8 && r.DstSelX() == 4 && r.DstSelY() == 5 && r.DstSelZ() == 0 && r.DstSelW() == 0 && r.Dfmt() == 11 && r.Nfmt() == 4) || (r.Stride() == 16 && r.DstSelX() == 4 && r.DstSelY() == 5 && r.DstSelZ() == 6 && r.DstSelW() == 7 && r.Dfmt() == 14 && r.Nfmt() == 7))); EXIT_NOT_IMPLEMENTED(!(r.MemoryType() == 0x00 || r.MemoryType() == 0x10 || r.MemoryType() == 0x6d)); auto addr = r.Base(); auto stride = r.Stride(); auto num_records = r.NumRecords(); auto size = stride * num_records; EXIT_NOT_IMPLEMENTED(size == 0); EXIT_NOT_IMPLEMENTED((size & 0x3u) != 0); bool read_only = (r.MemoryType() == 0x10); EXIT_NOT_IMPLEMENTED(read_only && !(storage_buffers.usages[i] == ShaderStorageUsage::ReadOnly || storage_buffers.usages[i] == ShaderStorageUsage::Constant)); StorageBufferGpuObject buf_info(stride, num_records, read_only); auto* buf = static_cast(GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, addr, size, buf_info)); EXIT_NOT_IMPLEMENTED(buf == nullptr); StorageBufferSet(buffer, buf); buffers[i] = buf; r.UpdateAddress(i); EXIT_NOT_IMPLEMENTED((r.Base() >> 32u) != 0); (*sgprs)[0] = r.fields[0]; (*sgprs)[1] = r.fields[1]; (*sgprs)[2] = r.fields[2]; (*sgprs)[3] = r.fields[3]; (*sgprs) += 4; } } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static void PrepareTextures(CommandBuffer* buffer, const ShaderTextureResources& textures, VulkanImage** images_sampled, VulkanImage** images_storage, uint32_t** sgprs, const bool* without_sampler) { KYTY_PROFILER_FUNCTION(); EXIT_IF(images_sampled == nullptr); EXIT_IF(images_storage == nullptr); EXIT_IF(sgprs == nullptr); EXIT_IF(*sgprs == nullptr); EXIT_IF(without_sampler == nullptr); int index_sampled = 0; int index_storage = 0; for (int i = 0; i < textures.textures_num; i++) { auto r = textures.textures[i]; EXIT_NOT_IMPLEMENTED(r.Base() == 0); EXIT_NOT_IMPLEMENTED(r.MinLod() != 0); EXIT_NOT_IMPLEMENTED(r.Dfmt() != 10 && r.Dfmt() != 37); EXIT_NOT_IMPLEMENTED(r.Nfmt() != 9 && r.Nfmt() != 0); // EXIT_NOT_IMPLEMENTED(r.Width() != 511); // EXIT_NOT_IMPLEMENTED(r.Height() != 511); EXIT_NOT_IMPLEMENTED(r.PerfMod() != 7 && r.PerfMod() != 0); EXIT_NOT_IMPLEMENTED(r.Interlaced() != false); // EXIT_NOT_IMPLEMENTED(r.DstSelX() != 4); // EXIT_NOT_IMPLEMENTED(r.DstSelY() != 5); // EXIT_NOT_IMPLEMENTED(r.DstSelZ() != 6); // EXIT_NOT_IMPLEMENTED(r.DstSelW() != 7); // EXIT_NOT_IMPLEMENTED(r.BaseLevel() != 0); // EXIT_NOT_IMPLEMENTED(r.LastLevel() != 0 && r.LastLevel() != 9); EXIT_NOT_IMPLEMENTED(!(r.TilingIdx() == 8 || r.TilingIdx() == 13 || r.TilingIdx() == 14)); // EXIT_NOT_IMPLEMENTED(!(r.LastLevel() == 0 && r.Pow2Pad() == false) && !(r.LastLevel() == 9 && r.Pow2Pad() == true)); EXIT_NOT_IMPLEMENTED(r.Type() != 9); EXIT_NOT_IMPLEMENTED(r.Depth() != 0); // EXIT_NOT_IMPLEMENTED(r.Pitch() != 511); EXIT_NOT_IMPLEMENTED(r.BaseArray() != 0); EXIT_NOT_IMPLEMENTED(r.LastArray() != 0); EXIT_NOT_IMPLEMENTED(r.MinLodWarn() != 0); EXIT_NOT_IMPLEMENTED(r.CounterBankId() != 0); EXIT_NOT_IMPLEMENTED(r.LodHdwCntEn() != false); EXIT_NOT_IMPLEMENTED(r.MemoryType() != 0x10 && r.MemoryType() != 0x6d); bool read_only = (r.MemoryType() == 0x10); EXIT_NOT_IMPLEMENTED(read_only && !(textures.usages[i] == ShaderTextureUsage::ReadOnly)); // EXIT_NOT_IMPLEMENTED(!read_only && !(textures.usages[i] == ShaderTextureUsage::ReadWrite)); auto addr = r.Base(); uint32_t size = 0; bool neo = Config::IsNeo(); auto width = r.Width() + 1; auto height = r.Height() + 1; auto pitch = r.Pitch() + 1; auto base_level = r.BaseLevel(); auto levels = r.LastLevel() + 1; auto tile = r.TilingIdx(); uint32_t swizzle = static_cast(r.DstSelX()) | (static_cast(r.DstSelY()) << 8u) | (static_cast(r.DstSelZ()) << 16u) | (static_cast(r.DstSelW()) << 24u); TileGetTextureSize(r.Dfmt(), r.Nfmt(), width, height, pitch, levels, tile, neo, &size, nullptr, nullptr, nullptr); EXIT_NOT_IMPLEMENTED(size == 0); auto rtex = FindRenderTexture(addr, size, true); EXIT_NOT_IMPLEMENTED(rtex.Size() > 1); VulkanImage* tex = (!rtex.IsEmpty() ? rtex.At(0) : nullptr); if (tex == nullptr) { if (without_sampler[i]) { StorageTextureObject vulkan_texture_info(r.Dfmt(), r.Nfmt(), width, height, pitch, base_level, levels, tile, neo, swizzle); tex = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), buffer, addr, size, vulkan_texture_info)); } else { TextureObject vulkan_texture_info(r.Dfmt(), r.Nfmt(), width, height, pitch, base_level, levels, tile, neo, swizzle); tex = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), buffer, addr, size, vulkan_texture_info)); } } EXIT_NOT_IMPLEMENTED(tex == nullptr); if (without_sampler[i]) { images_storage[index_storage] = tex; r.UpdateAddress(index_storage); index_storage++; } else { images_sampled[index_sampled] = tex; r.UpdateAddress(index_sampled); index_sampled++; } EXIT_NOT_IMPLEMENTED((r.Base() >> 32u) != 0); (*sgprs)[0] = r.fields[0]; (*sgprs)[1] = r.fields[1]; (*sgprs)[2] = r.fields[2]; (*sgprs)[3] = r.fields[3]; (*sgprs)[4] = r.fields[4]; (*sgprs)[5] = r.fields[5]; (*sgprs)[6] = r.fields[6]; (*sgprs)[7] = r.fields[7]; (*sgprs) += 8; } } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static void PrepareSamplers(const ShaderSamplerResources& samplers, uint64_t* sampler_ids, uint32_t** sgprs) { KYTY_PROFILER_FUNCTION(); EXIT_IF(sampler_ids == nullptr); EXIT_IF(sgprs == nullptr); EXIT_IF(*sgprs == nullptr); for (int i = 0; i < samplers.samplers_num; i++) { auto r = samplers.samplers[i]; EXIT_NOT_IMPLEMENTED(r.ClampX() != 0); EXIT_NOT_IMPLEMENTED(r.ClampY() != 0); EXIT_NOT_IMPLEMENTED(r.ClampZ() != 0); // EXIT_NOT_IMPLEMENTED(r.MaxAnisoRatio() != 0); EXIT_NOT_IMPLEMENTED(r.DepthCompareFunc() != 0); EXIT_NOT_IMPLEMENTED(r.ForceUnormCoords() != false); EXIT_NOT_IMPLEMENTED(r.AnisoThreshold() != 0); EXIT_NOT_IMPLEMENTED(r.McCoordTrunc() != false); EXIT_NOT_IMPLEMENTED(r.ForceDegamma() != false); EXIT_NOT_IMPLEMENTED(r.AnisoBias() != 0); EXIT_NOT_IMPLEMENTED(r.TruncCoord() != false); EXIT_NOT_IMPLEMENTED(r.DisableCubeWrap() != false); EXIT_NOT_IMPLEMENTED(r.FilterMode() != 0); // EXIT_NOT_IMPLEMENTED(r.MinLod() != 0); // EXIT_NOT_IMPLEMENTED(r.MaxLod() != 4095); EXIT_NOT_IMPLEMENTED(r.PerfMip() != 0); EXIT_NOT_IMPLEMENTED(r.PerfZ() != 0); EXIT_NOT_IMPLEMENTED(r.LodBias() != 0); EXIT_NOT_IMPLEMENTED(r.LodBiasSec() != 0); // EXIT_NOT_IMPLEMENTED(r.XyMagFilter() != 1); // EXIT_NOT_IMPLEMENTED(r.XyMinFilter() != 1); EXIT_NOT_IMPLEMENTED(r.ZFilter() != 1); // EXIT_NOT_IMPLEMENTED(r.MipFilter() != 0 && r.MipFilter() != 2); EXIT_NOT_IMPLEMENTED(r.BorderColorPtr() != 0); EXIT_NOT_IMPLEMENTED(r.BorderColorType() != 0); sampler_ids[i] = g_render_ctx->GetSamplerCache()->GetSamplerId(r); r.UpdateIndex(i); (*sgprs)[0] = r.fields[0]; (*sgprs)[1] = r.fields[1]; (*sgprs)[2] = r.fields[2]; (*sgprs)[3] = r.fields[3]; (*sgprs) += 4; } } static void PrepareGdsPointers(const ShaderGdsResources& gds_pointers, uint32_t** sgprs) { KYTY_PROFILER_FUNCTION(); EXIT_IF(sgprs == nullptr); EXIT_IF(*sgprs == nullptr); for (int i = 0; i < gds_pointers.pointers_num; i++) { auto r = gds_pointers.pointers[i]; EXIT_NOT_IMPLEMENTED(r.Size() != 4); (*sgprs)[i] = r.field; } if (gds_pointers.pointers_num > 0) { (*sgprs) += 4 * ((gds_pointers.pointers_num - 1) / 4 + 1); } } static void BindDescriptors(CommandBuffer* buffer, VkPipelineBindPoint pipeline_bind_point, VkPipelineLayout layout, const ShaderBindResources& bind, const ShaderBindParameters& bind_params, VkShaderStageFlags vk_stage, DescriptorCache::Stage stage) { KYTY_PROFILER_FUNCTION(); if (bind.push_constant_size > 0) { EXIT_NOT_IMPLEMENTED(bind.push_constant_size > DescriptorCache::PUSH_CONSTANTS_MAX * 4); EXIT_NOT_IMPLEMENTED(bind.storage_buffers.buffers_num > DescriptorCache::BUFFERS_MAX); EXIT_NOT_IMPLEMENTED((bind_params.textures2d_storage_num > DescriptorCache::TEXTURES_STORAGE_MAX) || (bind_params.textures2d_sampled_num > DescriptorCache::TEXTURES_SAMPLED_MAX)); EXIT_NOT_IMPLEMENTED(bind_params.textures2d_storage_num + bind_params.textures2d_sampled_num != bind.textures2D.textures_num); EXIT_NOT_IMPLEMENTED(bind.samplers.samplers_num > DescriptorCache::SAMPLERS_MAX); VulkanBuffer* storage_buffers[DescriptorCache::BUFFERS_MAX]; VulkanImage* textures2d_sampled[DescriptorCache::TEXTURES_SAMPLED_MAX]; VulkanImage* textures2d_storage[DescriptorCache::TEXTURES_STORAGE_MAX]; uint64_t samplers[DescriptorCache::SAMPLERS_MAX]; uint32_t sgprs[DescriptorCache::PUSH_CONSTANTS_MAX]; uint32_t* sgprs_ptr = sgprs; VulkanBuffer* gds_buffer = nullptr; if (bind.storage_buffers.buffers_num > 0) { PrepareStorageBuffers(buffer, bind.storage_buffers, storage_buffers, &sgprs_ptr); } if (bind.textures2D.textures_num > 0) { PrepareTextures(buffer, bind.textures2D, textures2d_sampled, textures2d_storage, &sgprs_ptr, bind_params.textures2d_without_sampler); } if (bind.samplers.samplers_num > 0) { PrepareSamplers(bind.samplers, samplers, &sgprs_ptr); } if (bind.gds_pointers.pointers_num > 0) { PrepareGdsPointers(bind.gds_pointers, &sgprs_ptr); gds_buffer = g_render_ctx->GetGdsBuffer()->GetBuffer(g_render_ctx->GetGraphicCtx()); } EXIT_IF(bind.push_constant_size != (sgprs_ptr - sgprs) * 4); auto* descriptor_set = g_render_ctx->GetDescriptorCache()->GetDescriptor( stage, storage_buffers, textures2d_sampled, textures2d_storage, samplers, &gds_buffer, bind, bind_params); EXIT_IF(descriptor_set == nullptr); auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()]; vkCmdBindDescriptorSets(vk_buffer, pipeline_bind_point, layout, bind.descriptor_set_slot, 1, &descriptor_set->set, 0, nullptr); vkCmdPushConstants(vk_buffer, layout, vk_stage, bind.push_constant_offset, bind.push_constant_size, sgprs); } } static void SetDynamicParams(VkCommandBuffer vk_buffer, VulkanPipeline* pipeline) { KYTY_PROFILER_FUNCTION(); EXIT_IF(pipeline == nullptr); EXIT_IF(pipeline->static_params == nullptr); EXIT_IF(pipeline->dynamic_params == nullptr); if (pipeline->static_params->stencil_test_enable) { if (pipeline->dynamic_params->vk_dynamic_state_stencil_compare_mask) { vkCmdSetStencilCompareMask(vk_buffer, VK_STENCIL_FACE_FRONT_BIT, pipeline->dynamic_params->stencil_front.compareMask); vkCmdSetStencilCompareMask(vk_buffer, VK_STENCIL_FACE_BACK_BIT, pipeline->dynamic_params->stencil_back.compareMask); } if (pipeline->dynamic_params->vk_dynamic_state_stencil_write_mask) { vkCmdSetStencilWriteMask(vk_buffer, VK_STENCIL_FACE_FRONT_BIT, pipeline->dynamic_params->stencil_front.writeMask); vkCmdSetStencilWriteMask(vk_buffer, VK_STENCIL_FACE_BACK_BIT, pipeline->dynamic_params->stencil_back.writeMask); } if (pipeline->dynamic_params->vk_dynamic_state_stencil_reference) { vkCmdSetStencilReference(vk_buffer, VK_STENCIL_FACE_FRONT_BIT, pipeline->dynamic_params->stencil_front.reference); vkCmdSetStencilReference(vk_buffer, VK_STENCIL_FACE_BACK_BIT, pipeline->dynamic_params->stencil_back.reference); } } } void GraphicsRenderDrawIndex(CommandBuffer* buffer, HardwareContext* ctx, UserConfig* ucfg, uint32_t index_type_and_size, uint32_t index_count, const void* index_addr, uint32_t flags, uint32_t type) { KYTY_PROFILER_FUNCTION(); EXIT_IF(ctx == nullptr); EXIT_IF(ucfg == nullptr); EXIT_IF(g_render_ctx == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); if (const auto& vs = ctx->GetVs(); !vs.vs_embedded && ShaderIsDisabled(vs.vs_regs.GetGpuAddress())) { return; } if (const auto& ps = ctx->GetPs(); !ps.ps_embedded && ShaderIsDisabled(ps.ps_regs.data_addr)) { return; } // const auto& rt = ctx->GetRenderTargets(0); const auto& bc = ctx->GetBlendControl(0); uc_print("GraphicsRenderDrawIndex():UserConfig:", *ucfg); hw_print(*ctx); hw_check(*ctx); EXIT_NOT_IMPLEMENTED(ucfg->GetPrimType() != 4); EXIT_NOT_IMPLEMENTED(ctx->GetShaderStages() != 0); printf("GraphicsRenderDrawIndex():Parameters:\n"); printf("\t index_type_and_size = 0x%08" PRIx32 "\n", index_type_and_size); printf("\t index_count = 0x%08" PRIx32 "\n", index_count); printf("\t index_addr = 0x%016" PRIx64 "\n", reinterpret_cast(index_addr)); printf("\t flags = 0x%08" PRIx32 "\n", flags); printf("\t type = 0x%08" PRIx32 "\n", type); VkIndexType index_type = VK_INDEX_TYPE_UINT16; uint64_t index_size = 0; switch (index_type_and_size) { case 0: index_type = VK_INDEX_TYPE_UINT16; index_size = 2 * index_count; break; case 1: index_type = VK_INDEX_TYPE_UINT32; index_size = 4 * index_count; break; default: EXIT("unknown index_type_and_size: %u\n", index_type_and_size); } EXIT_NOT_IMPLEMENTED(flags != 0); EXIT_NOT_IMPLEMENTED(type != 1); RenderDepthInfo depth_info; FindRenderDepthInfo(buffer, *ctx, &depth_info); RenderColorInfo color_info; FindRenderColorInfo(buffer, *ctx, &color_info); auto* framebuffer = g_render_ctx->GetFramebufferCache()->CreateFramebuffer(&color_info, &depth_info); EXIT_NOT_IMPLEMENTED(framebuffer == nullptr); EXIT_NOT_IMPLEMENTED(framebuffer->render_pass == nullptr); auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()]; ShaderVertexInputInfo vs_input_info; ShaderGetInputInfoVS(&ctx->GetVs(), &vs_input_info); ShaderPixelInputInfo ps_input_info; ShaderGetInputInfoPS(&ctx->GetPs(), &vs_input_info, &ps_input_info); EXIT_NOT_IMPLEMENTED(vs_input_info.buffers_num > 1); auto* pipeline = g_render_ctx->GetPipelineCache()->CreatePipeline(framebuffer, &color_info, &depth_info, &vs_input_info, &ctx->GetVs(), &ps_input_info, &ctx->GetPs(), VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ctx->GetRenderTargetMask(), ctx->GetModeControl(), bc, ctx->GetBlendColor(), ctx->GetScreenViewport()); vkCmdBindPipeline(vk_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); SetDynamicParams(vk_buffer, pipeline); for (int i = 0; i < vs_input_info.buffers_num; i++) { const auto& b = vs_input_info.buffers[i]; uint64_t addr = b.addr; uint64_t size = b.stride * b.num_records; auto* vertices = static_cast(GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, addr, size, VertexBufferGpuObject())); VkDeviceSize offset = 0; vkCmdBindVertexBuffers(vk_buffer, i, 1, &vertices->buffer, &offset); } BindDescriptors(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline_layout, vs_input_info.bind, pipeline->additional_params->vs_bind, VK_SHADER_STAGE_VERTEX_BIT, DescriptorCache::Stage::Vertex); BindDescriptors(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline_layout, ps_input_info.bind, pipeline->additional_params->ps_bind, VK_SHADER_STAGE_FRAGMENT_BIT, DescriptorCache::Stage::Pixel); VulkanBuffer* indices = static_cast(GpuMemoryCreateObject( g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(index_addr), index_size, IndexBufferGpuObject())); EXIT_NOT_IMPLEMENTED(indices == nullptr); vkCmdBindIndexBuffer(vk_buffer, indices->buffer, 0, index_type); buffer->BeginRenderPass(framebuffer, &color_info, &depth_info); vkCmdDrawIndexed(vk_buffer, index_count, 1, 0, 0, 0); buffer->EndRenderPass(); InvalidateMemoryObject(color_info); InvalidateMemoryObject(depth_info); } // NOLINTNEXTLINE(readability-function-cognitive-complexity) void GraphicsRenderDrawIndexAuto(CommandBuffer* buffer, HardwareContext* ctx, UserConfig* ucfg, uint32_t index_count, uint32_t flags) { KYTY_PROFILER_FUNCTION(); EXIT_IF(ctx == nullptr || ucfg == nullptr); EXIT_IF(g_render_ctx == nullptr); EXIT_IF(buffer == nullptr || buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); if (const auto& vs = ctx->GetVs(); !vs.vs_embedded && ShaderIsDisabled(vs.vs_regs.GetGpuAddress())) { return; } if (const auto& ps = ctx->GetPs(); !ps.ps_embedded && ShaderIsDisabled(ps.ps_regs.data_addr)) { return; } // const auto& rt = ctx->GetRenderTargets(0); const auto& bc = ctx->GetBlendControl(0); uc_print("GraphicsRenderDrawIndexAuto():UserConfig:", *ucfg); hw_print(*ctx); hw_check(*ctx); printf("GraphicsRenderDrawIndex():Parameters:\n"); printf("\t index_count = 0x%08" PRIx32 "\n", index_count); printf("\t flags = 0x%08" PRIx32 "\n", flags); EXIT_NOT_IMPLEMENTED(flags != 0); EXIT_NOT_IMPLEMENTED(ctx->GetShaderStages() != 0); RenderDepthInfo depth_info; FindRenderDepthInfo(buffer, *ctx, &depth_info); RenderColorInfo color_info; FindRenderColorInfo(buffer, *ctx, &color_info); auto* framebuffer = g_render_ctx->GetFramebufferCache()->CreateFramebuffer(&color_info, &depth_info); EXIT_NOT_IMPLEMENTED(framebuffer == nullptr); EXIT_NOT_IMPLEMENTED(framebuffer->render_pass == nullptr); auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()]; VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; switch (ucfg->GetPrimType()) { case 4: topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; break; case 17: topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; break; case 19: topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; break; default: EXIT("unknown primitive type: %u\n", ucfg->GetPrimType()); } const auto& vertex_shader_info = ctx->GetVs(); const auto& pixel_shader_info = ctx->GetPs(); ShaderVertexInputInfo vs_input_info; ShaderGetInputInfoVS(&vertex_shader_info, &vs_input_info); ShaderPixelInputInfo ps_input_info; ShaderGetInputInfoPS(&pixel_shader_info, &vs_input_info, &ps_input_info); EXIT_NOT_IMPLEMENTED(vs_input_info.buffers_num > 1); auto* pipeline = g_render_ctx->GetPipelineCache()->CreatePipeline( framebuffer, &color_info, &depth_info, &vs_input_info, &vertex_shader_info, &ps_input_info, &pixel_shader_info, topology, ctx->GetRenderTargetMask(), ctx->GetModeControl(), bc, ctx->GetBlendColor(), ctx->GetScreenViewport()); vkCmdBindPipeline(vk_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); SetDynamicParams(vk_buffer, pipeline); for (int i = 0; i < vs_input_info.buffers_num; i++) { const auto& b = vs_input_info.buffers[i]; uint64_t addr = b.addr; uint64_t size = b.stride * b.num_records; auto* vertices = static_cast(GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, addr, size, VertexBufferGpuObject())); VkDeviceSize offset = 0; vkCmdBindVertexBuffers(vk_buffer, i, 1, &vertices->buffer, &offset); } BindDescriptors(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline_layout, vs_input_info.bind, pipeline->additional_params->vs_bind, VK_SHADER_STAGE_VERTEX_BIT, DescriptorCache::Stage::Vertex); BindDescriptors(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline_layout, ps_input_info.bind, pipeline->additional_params->ps_bind, VK_SHADER_STAGE_FRAGMENT_BIT, DescriptorCache::Stage::Pixel); buffer->BeginRenderPass(framebuffer, &color_info, &depth_info); switch (ucfg->GetPrimType()) { case 4: vkCmdDraw(vk_buffer, index_count, 1, 0, 0); break; case 17: EXIT_NOT_IMPLEMENTED(!(/*vertex_shader_info.vs_embedded && vertex_shader_info.vs_embedded_id == 0 &&*/ index_count == 3 && vs_input_info.buffers_num == 0)); vkCmdDraw(vk_buffer, 4, 1, 0, 0); break; case 19: EXIT_NOT_IMPLEMENTED(index_count != 4); vkCmdDraw(vk_buffer, 4, 1, 0, 0); break; default: EXIT("unknown primitive type: %u\n", ucfg->GetPrimType()); } buffer->EndRenderPass(); InvalidateMemoryObject(color_info); InvalidateMemoryObject(depth_info); } void GraphicsRenderDispatchDirect(CommandBuffer* buffer, HardwareContext* ctx, uint32_t thread_group_x, uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode) { EXIT_IF(ctx == nullptr); EXIT_IF(g_render_ctx == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); if (ShaderIsDisabled(ctx->GetCs().cs_regs.data_addr)) { return; } EXIT_NOT_IMPLEMENTED(mode != 0); const auto& cs_regs = ctx->GetCs(); ShaderComputeInputInfo input_info; ShaderGetInputInfoCS(&cs_regs, &input_info); auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()]; auto* pipeline = g_render_ctx->GetPipelineCache()->CreatePipeline(&input_info, &ctx->GetCs()); vkCmdBindPipeline(vk_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); SetDynamicParams(vk_buffer, pipeline); BindDescriptors(buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline_layout, input_info.bind, pipeline->additional_params->cs_bind, VK_SHADER_STAGE_COMPUTE_BIT, DescriptorCache::Stage::Compute); vkCmdDispatch(vk_buffer, thread_group_x, thread_group_y, thread_group_z); } void GraphicsRenderMemoryBarrier(CommandBuffer* buffer) { EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()]; VkMemoryBarrier mem_barrier {}; mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; mem_barrier.pNext = nullptr; mem_barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; mem_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; vkCmdPipelineBarrier(vk_buffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &mem_barrier, 0, nullptr, 0, nullptr); } void GraphicsRenderRenderTextureBarrier(CommandBuffer* buffer, uint64_t vaddr, uint64_t size) { EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()]; auto images = FindRenderTexture(vaddr, size, false); for (auto* image: images) { EXIT_NOT_IMPLEMENTED(image == nullptr); if (image->layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { VkImageMemoryBarrier image_memory_barrier {}; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; image_memory_barrier.oldLayout = image->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = image->image; image_memory_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; image_memory_barrier.subresourceRange.baseMipLevel = 0; image_memory_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; image_memory_barrier.subresourceRange.baseArrayLayer = 0; image_memory_barrier.subresourceRange.layerCount = 1; vkCmdPipelineBarrier(vk_buffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); image->layout = image_memory_barrier.newLayout; } } } void GraphicsRenderWriteAtEndOfPipe(CommandBuffer* buffer, uint32_t* dst_gpu_addr, uint32_t value) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); Graphics::LabelGpuObject label_info(value, nullptr, nullptr); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 4, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipeGds(CommandBuffer* buffer, uint32_t* dst_gpu_addr, uint32_t dw_offset, uint32_t dw_num) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); uint64_t args[4] = {static_cast(dw_offset), static_cast(dw_num), reinterpret_cast(dst_gpu_addr), 0}; Graphics::LabelGpuObject label_info( 0, [](const uint64_t* args) { auto dw_offset = static_cast(args[0]); auto dw_num = static_cast(args[1]); auto* dst_gpu_addr = reinterpret_cast(args[2]); g_render_ctx->GetGdsBuffer()->Read(g_render_ctx->GetGraphicCtx(), dst_gpu_addr, dw_offset, dw_num); return false; }, nullptr, args); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 4, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipe(CommandBuffer* buffer, uint64_t* dst_gpu_addr, uint64_t value) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); Graphics::LabelGpuObject label_info(value, nullptr, nullptr); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 8, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipeClockCounter(CommandBuffer* buffer, uint64_t* dst_gpu_addr) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); uint64_t args[4] = {reinterpret_cast(dst_gpu_addr), 0, 0, 0}; Graphics::LabelGpuObject label_info( 0, [](const uint64_t* args) { auto* dst_gpu_addr = reinterpret_cast(args[0]); EXIT_IF(dst_gpu_addr == nullptr); *dst_gpu_addr = LibKernel::KernelReadTsc(); printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- Clock: 0x%016" PRIx64 "\n" FG_DEFAULT, reinterpret_cast(dst_gpu_addr), *dst_gpu_addr); return false; }, nullptr, args); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 8, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipeWithWriteBack(CommandBuffer* buffer, uint64_t* dst_gpu_addr, uint64_t value) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); Graphics::LabelGpuObject label_info( value, [](const uint64_t* /*args*/) { EXIT_IF(g_render_ctx == nullptr); GpuMemoryWriteBack(g_render_ctx->GetGraphicCtx()); return true; }, nullptr); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 8, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipeWithInterruptWriteBack(CommandBuffer* buffer, uint64_t* dst_gpu_addr, uint64_t value) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); Graphics::LabelGpuObject label_info( value, [](const uint64_t* /*args*/) { EXIT_IF(g_render_ctx == nullptr); GpuMemoryWriteBack(g_render_ctx->GetGraphicCtx()); return true; }, [](const uint64_t* /*args*/) { EXIT_IF(g_render_ctx == nullptr); g_render_ctx->TriggerEopEvent(); return true; }); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 8, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipeWithInterrupt(CommandBuffer* buffer, uint64_t* dst_gpu_addr, uint64_t value) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); Graphics::LabelGpuObject label_info(value, nullptr, [](const uint64_t* /*args*/) { EXIT_IF(g_render_ctx == nullptr); g_render_ctx->TriggerEopEvent(); return true; }); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 8, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipeWithInterruptWriteBackFlip(CommandBuffer* buffer, uint32_t* dst_gpu_addr, uint32_t value, int handle, int index, int flip_mode, int64_t flip_arg) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); uint64_t args[] = {static_cast(handle), static_cast(index), static_cast(flip_mode), static_cast(flip_arg)}; Graphics::LabelGpuObject label_info( value, [](const uint64_t* args) { EXIT_IF(g_render_ctx == nullptr); GpuMemoryWriteBack(g_render_ctx->GetGraphicCtx()); int handle = static_cast(args[0]); int index = static_cast(args[1]); int flip_mode = static_cast(args[2]); int64_t flip_arg = static_cast(args[3]); VideoOut::VideoOutSubmitFlip(handle, index, flip_mode, flip_arg); return true; }, [](const uint64_t* /*args*/) { EXIT_IF(g_render_ctx == nullptr); g_render_ctx->TriggerEopEvent(); return true; }, args); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 4, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteAtEndOfPipeWithFlip(CommandBuffer* buffer, uint32_t* dst_gpu_addr, uint32_t value, int handle, int index, int flip_mode, int64_t flip_arg) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(dst_gpu_addr == nullptr); EXIT_IF(buffer == nullptr); EXIT_IF(buffer->IsInvalid()); Core::LockGuard lock(g_render_ctx->GetMutex()); uint64_t args[] = {static_cast(handle), static_cast(index), static_cast(flip_mode), static_cast(flip_arg)}; Graphics::LabelGpuObject label_info( value, [](const uint64_t* args) { int handle = static_cast(args[0]); int index = static_cast(args[1]); int flip_mode = static_cast(args[2]); int64_t flip_arg = static_cast(args[3]); VideoOut::VideoOutSubmitFlip(handle, index, flip_mode, flip_arg); return true; }, nullptr, args); auto* label = static_cast( GpuMemoryCreateObject(g_render_ctx->GetGraphicCtx(), nullptr, reinterpret_cast(dst_gpu_addr), 4, label_info)); LabelSet(buffer, label); } void GraphicsRenderWriteBack() { Core::LockGuard lock(g_render_ctx->GetMutex()); EXIT_IF(g_render_ctx == nullptr); GpuMemoryWriteBack(g_render_ctx->GetGraphicCtx()); } static void eop_event_reset_func(LibKernel::EventQueue::KernelEqueueEvent* event) { EXIT_IF(event == nullptr); event->triggered = false; event->event.fflags = 0; event->event.data = 0; } static void eop_event_delete_func(LibKernel::EventQueue::KernelEqueue eq, LibKernel::EventQueue::KernelEqueueEvent* event) { EXIT_IF(event == nullptr); EXIT_IF(g_render_ctx == nullptr); EXIT_NOT_IMPLEMENTED(event->event.ident != GRAPHICS_EVENT_EOP); EXIT_NOT_IMPLEMENTED(event->event.filter != LibKernel::EventQueue::KERNEL_EVFILT_GRAPHICS); g_render_ctx->DeleteEopEq(eq); } static void eop_event_trigger_func(LibKernel::EventQueue::KernelEqueueEvent* event, void* trigger_data) { EXIT_IF(event == nullptr); event->triggered = true; event->event.fflags++; event->event.data = reinterpret_cast(trigger_data); } int GraphicsRenderAddEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id, void* udata) { EXIT_IF(g_render_ctx == nullptr); EXIT_NOT_IMPLEMENTED(id != GRAPHICS_EVENT_EOP); LibKernel::EventQueue::KernelEqueueEvent event; event.triggered = false; event.event.ident = GRAPHICS_EVENT_EOP; event.event.filter = LibKernel::EventQueue::KERNEL_EVFILT_GRAPHICS; event.event.udata = udata; event.event.fflags = 0; event.event.data = 0; event.filter.delete_event_func = eop_event_delete_func; event.filter.reset_func = eop_event_reset_func; event.filter.trigger_func = eop_event_trigger_func; event.filter.data = nullptr; int result = LibKernel::EventQueue::KernelAddEvent(eq, event); g_render_ctx->AddEopEq(eq); return result; } int GraphicsRenderDeleteEqEvent(LibKernel::EventQueue::KernelEqueue eq, int id) { EXIT_IF(g_render_ctx == nullptr); EXIT_NOT_IMPLEMENTED(id != GRAPHICS_EVENT_EOP); int result = LibKernel::EventQueue::KernelDeleteEvent(eq, GRAPHICS_EVENT_EOP, LibKernel::EventQueue::KERNEL_EVFILT_GRAPHICS); g_render_ctx->DeleteEopEq(eq); return result; } void GraphicsRenderClearGds(uint64_t dw_offset, uint32_t dw_num, uint32_t clear_value) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(g_render_ctx->GetGdsBuffer() == nullptr); g_render_ctx->GetGdsBuffer()->Clear(g_render_ctx->GetGraphicCtx(), dw_offset, dw_num, clear_value); } void GraphicsRenderReadGds(uint32_t* dst, uint32_t dw_offset, uint32_t dw_size) { EXIT_IF(g_render_ctx == nullptr); EXIT_IF(g_render_ctx->GetGdsBuffer() == nullptr); g_render_ctx->GetGdsBuffer()->Read(g_render_ctx->GetGraphicCtx(), dst, dw_offset, dw_size); } void GraphicsRenderMemoryFree(uint64_t vaddr, uint64_t size) { GpuMemoryFree(g_render_ctx->GetGraphicCtx(), vaddr, size, false); } bool CommandBuffer::IsInvalid() const { EXIT_IF(g_render_ctx == nullptr); if (m_pool != nullptr) { Core::LockGuard lock(m_pool->mutex); return (m_index == static_cast(-1) || m_index >= m_pool->buffers_count); } return true; } void CommandBuffer::Allocate() { EXIT_IF(!IsInvalid()); m_pool = g_command_pool.GetPool(); Core::LockGuard lock(m_pool->mutex); for (uint32_t i = 0; i < m_pool->buffers_count; i++) { if (!m_pool->busy[i]) { m_pool->busy[i] = true; vkResetCommandBuffer(m_pool->buffers[i], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); m_index = i; break; } } EXIT_NOT_IMPLEMENTED(IsInvalid()); } void CommandBuffer::Free() { EXIT_IF(IsInvalid()); Core::LockGuard lock(m_pool->mutex); WaitForFence(); m_pool->busy[m_index] = false; vkResetCommandBuffer(m_pool->buffers[m_index], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); m_index = static_cast(-1); EXIT_NOT_IMPLEMENTED(!IsInvalid()); } void CommandBuffer::Begin() const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; VkCommandBufferBeginInfo begin_info {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.pNext = nullptr; begin_info.flags = 0; begin_info.pInheritanceInfo = nullptr; auto result = vkBeginCommandBuffer(buffer, &begin_info); EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::End() const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; auto result = vkEndCommandBuffer(buffer); EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::Execute() { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; auto* fence = m_pool->fences[m_index]; VkSubmitInfo submit_info {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = nullptr; submit_info.pWaitDstStageMask = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffer; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = nullptr; EXIT_IF(m_queue < 0 || m_queue >= GraphicContext::QUEUES_NUM); auto* queue = g_render_ctx->GetGraphicCtx()->queue[m_queue]; auto result = vkQueueSubmit(queue, 1, &submit_info, fence); m_execute = true; EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::ExecuteWithSemaphore() { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; auto* fence = m_pool->fences[m_index]; // auto* semaphore = m_pool->semaphores[m_index]; VkSubmitInfo submit_info {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = nullptr; submit_info.pWaitDstStageMask = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffer; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &m_pool->semaphores[m_index]; EXIT_IF(m_queue < 0 || m_queue >= GraphicContext::QUEUES_NUM); auto* queue = g_render_ctx->GetGraphicCtx()->queue[m_queue]; auto result = vkQueueSubmit(queue, 1, &submit_info, fence); m_execute = true; EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::WaitForFence() { EXIT_IF(IsInvalid()); if (m_execute) { auto* device = g_render_ctx->GetGraphicCtx()->device; vkWaitForFences(device, 1, &m_pool->fences[m_index], VK_TRUE, UINT64_MAX); vkResetFences(device, 1, &m_pool->fences[m_index]); m_execute = false; } } void CommandBuffer::WaitForFenceAndReset() { EXIT_IF(IsInvalid()); if (m_execute) { auto* device = g_render_ctx->GetGraphicCtx()->device; vkWaitForFences(device, 1, &m_pool->fences[m_index], VK_TRUE, UINT64_MAX); vkResetFences(device, 1, &m_pool->fences[m_index]); vkResetCommandBuffer(m_pool->buffers[m_index], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); m_execute = false; } } void CommandBuffer::BeginRenderPass(VulkanFramebuffer* framebuffer, RenderColorInfo* color, RenderDepthInfo* depth) const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; EXIT_IF(framebuffer == nullptr); bool with_depth = (depth->format != VK_FORMAT_UNDEFINED && depth->vulkan_buffer != nullptr); bool with_color = (color->vulkan_buffer != nullptr); EXIT_NOT_IMPLEMENTED(!with_depth && !with_color); VkClearValue clears[2]; clears[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; clears[1].depthStencil = {depth->depth_clear_value, depth->stencil_clear_value}; VkExtent2D extent = (with_color ? color->vulkan_buffer->extent : depth->vulkan_buffer->extent); VkRenderPassBeginInfo render_pass_info {}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_info.pNext = nullptr; render_pass_info.renderPass = framebuffer->render_pass; render_pass_info.framebuffer = framebuffer->framebuffer; render_pass_info.renderArea.offset = {0, 0}; render_pass_info.renderArea.extent = extent; render_pass_info.clearValueCount = with_depth ? 2 : 1; render_pass_info.pClearValues = clears; if (with_color && color->vulkan_buffer->layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { VkImageMemoryBarrier image_memory_barrier {}; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; image_memory_barrier.oldLayout = color->vulkan_buffer->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = color->vulkan_buffer->image; image_memory_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; image_memory_barrier.subresourceRange.baseMipLevel = 0; image_memory_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; image_memory_barrier.subresourceRange.baseArrayLayer = 0; image_memory_barrier.subresourceRange.layerCount = 1; vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); color->vulkan_buffer->layout = image_memory_barrier.newLayout; } vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); } void CommandBuffer::EndRenderPass() const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; vkCmdEndRenderPass(buffer); } } // namespace Kyty::Libs::Graphics #endif // KYTY_EMU_ENABLED