CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/Vulkan/thin3d_vulkan.cpp
Views: 1401
// Copyright (c) 2015- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <cstdio>18#include <vector>19#include <string>20#include <map>2122#include "Common/Log.h"23#include "Common/StringUtils.h"24#include "Common/System/Display.h"25#include "Common/Math/lin/matrix4x4.h"26#include "Common/Data/Convert/SmallDataConvert.h"27#include "Common/GPU/thin3d.h"28#include "Common/GPU/Vulkan/VulkanRenderManager.h"29#include "Common/GPU/Vulkan/VulkanContext.h"30#include "Common/GPU/Vulkan/VulkanImage.h"31#include "Common/GPU/Vulkan/VulkanMemory.h"32#include "Common/GPU/Vulkan/VulkanLoader.h"33#include "Common/Thread/Promise.h"3435// For descriptor set 0 (the only one), we use a simple descriptor set for all thin3d rendering: 1 UBO binding point, 3 combined texture/samples.36//37// binding 0 - uniform buffer38// binding 1 - texture/sampler39// binding 2 - texture/sampler40// binding 3 - texture/sampler41//42// Vertex data lives in a separate namespace (location = 0, 1, etc).4344using namespace PPSSPP_VK;4546namespace Draw {4748// This can actually be replaced with a cast as the values are in the right order.49static const VkCompareOp compToVK[] = {50VK_COMPARE_OP_NEVER,51VK_COMPARE_OP_LESS,52VK_COMPARE_OP_EQUAL,53VK_COMPARE_OP_LESS_OR_EQUAL,54VK_COMPARE_OP_GREATER,55VK_COMPARE_OP_NOT_EQUAL,56VK_COMPARE_OP_GREATER_OR_EQUAL,57VK_COMPARE_OP_ALWAYS58};5960// So can this.61static const VkBlendOp blendEqToVk[] = {62VK_BLEND_OP_ADD,63VK_BLEND_OP_SUBTRACT,64VK_BLEND_OP_REVERSE_SUBTRACT,65VK_BLEND_OP_MIN,66VK_BLEND_OP_MAX,67};6869static const VkBlendFactor blendFactorToVk[] = {70VK_BLEND_FACTOR_ZERO,71VK_BLEND_FACTOR_ONE,72VK_BLEND_FACTOR_SRC_COLOR,73VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,74VK_BLEND_FACTOR_DST_COLOR,75VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,76VK_BLEND_FACTOR_SRC_ALPHA,77VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,78VK_BLEND_FACTOR_DST_ALPHA,79VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,80VK_BLEND_FACTOR_CONSTANT_COLOR,81VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,82VK_BLEND_FACTOR_CONSTANT_ALPHA,83VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,84VK_BLEND_FACTOR_SRC1_COLOR,85VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR,86VK_BLEND_FACTOR_SRC1_ALPHA,87VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,88};8990static const VkLogicOp logicOpToVK[] = {91VK_LOGIC_OP_CLEAR,92VK_LOGIC_OP_SET,93VK_LOGIC_OP_COPY,94VK_LOGIC_OP_COPY_INVERTED,95VK_LOGIC_OP_NO_OP,96VK_LOGIC_OP_INVERT,97VK_LOGIC_OP_AND,98VK_LOGIC_OP_NAND,99VK_LOGIC_OP_OR,100VK_LOGIC_OP_NOR,101VK_LOGIC_OP_XOR,102VK_LOGIC_OP_EQUIVALENT,103VK_LOGIC_OP_AND_REVERSE,104VK_LOGIC_OP_AND_INVERTED,105VK_LOGIC_OP_OR_REVERSE,106VK_LOGIC_OP_OR_INVERTED,107};108109static const VkPrimitiveTopology primToVK[] = {110VK_PRIMITIVE_TOPOLOGY_POINT_LIST,111VK_PRIMITIVE_TOPOLOGY_LINE_LIST,112VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,113VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,114VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,115VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,116// Tesselation shader primitive.117VK_PRIMITIVE_TOPOLOGY_PATCH_LIST,118// The rest are for geometry shaders only.119VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,120VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,121VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,122VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,123};124125126static const VkStencilOp stencilOpToVK[8] = {127VK_STENCIL_OP_KEEP,128VK_STENCIL_OP_ZERO,129VK_STENCIL_OP_REPLACE,130VK_STENCIL_OP_INCREMENT_AND_CLAMP,131VK_STENCIL_OP_DECREMENT_AND_CLAMP,132VK_STENCIL_OP_INVERT,133VK_STENCIL_OP_INCREMENT_AND_WRAP,134VK_STENCIL_OP_DECREMENT_AND_WRAP,135};136137class VKBlendState : public BlendState {138public:139VkPipelineColorBlendStateCreateInfo info{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };140std::vector<VkPipelineColorBlendAttachmentState> attachments;141};142143class VKDepthStencilState : public DepthStencilState {144public:145VkPipelineDepthStencilStateCreateInfo info{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };146};147148class VKRasterState : public RasterState {149public:150VKRasterState(VulkanContext *vulkan, const RasterStateDesc &desc) {151cullFace = desc.cull;152frontFace = desc.frontFace;153}154Facing frontFace;155CullMode cullFace;156157void ToVulkan(VkPipelineRasterizationStateCreateInfo *info) const {158memset(info, 0, sizeof(*info));159info->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;160info->frontFace = frontFace == Facing::CCW ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE;161switch (cullFace) {162case CullMode::BACK: info->cullMode = VK_CULL_MODE_BACK_BIT; break;163case CullMode::FRONT: info->cullMode = VK_CULL_MODE_FRONT_BIT; break;164case CullMode::FRONT_AND_BACK: info->cullMode = VK_CULL_MODE_FRONT_AND_BACK; break;165case CullMode::NONE: info->cullMode = VK_CULL_MODE_NONE; break;166}167info->polygonMode = VK_POLYGON_MODE_FILL;168info->lineWidth = 1.0f;169}170};171172VkShaderStageFlagBits StageToVulkan(ShaderStage stage) {173switch (stage) {174case ShaderStage::Vertex: return VK_SHADER_STAGE_VERTEX_BIT;175case ShaderStage::Geometry: return VK_SHADER_STAGE_GEOMETRY_BIT;176case ShaderStage::Compute: return VK_SHADER_STAGE_COMPUTE_BIT;177case ShaderStage::Fragment: return VK_SHADER_STAGE_FRAGMENT_BIT;178}179return VK_SHADER_STAGE_FRAGMENT_BIT;180}181182// Not registering this as a resource holder, instead the pipeline is registered. It will183// invoke Compile again to recreate the shader then link them together.184class VKShaderModule : public ShaderModule {185public:186VKShaderModule(ShaderStage stage, const std::string &tag) : stage_(stage), tag_(tag) {187vkstage_ = StageToVulkan(stage);188}189bool Compile(VulkanContext *vulkan, ShaderLanguage language, const uint8_t *data, size_t size);190const std::string &GetSource() const { return source_; }191~VKShaderModule() {192if (module_) {193VkShaderModule shaderModule = module_->BlockUntilReady();194vulkan_->Delete().QueueDeleteShaderModule(shaderModule);195vulkan_->Delete().QueueCallback([](VulkanContext *context, void *m) {196auto module = (Promise<VkShaderModule> *)m;197delete module;198}, module_);199}200}201Promise<VkShaderModule> *Get() const { return module_; }202ShaderStage GetStage() const override {203return stage_;204}205206private:207VulkanContext *vulkan_ = nullptr;208Promise<VkShaderModule> *module_ = nullptr;209VkShaderStageFlagBits vkstage_;210bool ok_ = false;211ShaderStage stage_;212std::string source_; // So we can recompile in case of context loss.213std::string tag_;214};215216bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, const uint8_t *data, size_t size) {217// We'll need this to free it later.218vulkan_ = vulkan;219source_ = (const char *)data;220std::vector<uint32_t> spirv;221std::string errorMessage;222if (!GLSLtoSPV(vkstage_, source_.c_str(), GLSLVariant::VULKAN, spirv, &errorMessage)) {223WARN_LOG(Log::G3D, "Shader compile to module failed (%s): %s", tag_.c_str(), errorMessage.c_str());224return false;225}226227// Just for kicks, sanity check the SPIR-V. The disasm isn't perfect228// but gives you some idea of what's going on.229#if 0230std::string disasm;231if (DisassembleSPIRV(spirv, &disasm)) {232OutputDebugStringA(disasm.c_str());233}234#endif235236VkShaderModule shaderModule = VK_NULL_HANDLE;237if (vulkan->CreateShaderModule(spirv, &shaderModule, tag_.c_str())) {238module_ = Promise<VkShaderModule>::AlreadyDone(shaderModule);239ok_ = true;240} else {241WARN_LOG(Log::G3D, "vkCreateShaderModule failed (%s)", tag_.c_str());242ok_ = false;243}244return ok_;245}246247class VKInputLayout : public InputLayout {248public:249VkVertexInputBindingDescription binding;250std::vector<VkVertexInputAttributeDescription> attributes;251VkPipelineVertexInputStateCreateInfo visc;252};253254class VKPipeline : public Pipeline {255public:256VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags, const char *tag) : vulkan_(vulkan), flags(_flags), tag_(tag) {257uboSize_ = (int)size;258ubo_ = new uint8_t[uboSize_];259vkrDesc = new VKRGraphicsPipelineDesc();260}261~VKPipeline() {262if (pipeline) {263pipeline->QueueForDeletion(vulkan_);264}265for (auto dep : deps) {266dep->Release();267}268delete[] ubo_;269vkrDesc->Release();270}271272void SetDynamicUniformData(const void *data, size_t size) {273_dbg_assert_(size <= uboSize_);274memcpy(ubo_, data, size);275}276277// Returns the binding offset, and the VkBuffer to bind.278size_t PushUBO(VulkanPushPool *buf, VulkanContext *vulkan, VkBuffer *vkbuf) {279return buf->Push(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment, vkbuf);280}281282int GetUBOSize() const {283return uboSize_;284}285286VKRGraphicsPipeline *pipeline = nullptr;287VKRGraphicsPipelineDesc *vkrDesc = nullptr;288PipelineFlags flags;289290std::vector<VKShaderModule *> deps;291292int stride = 0;293int dynamicUniformSize = 0;294295bool usesStencil = false;296297private:298VulkanContext *vulkan_;299uint8_t *ubo_;300int uboSize_;301std::string tag_;302};303304class VKTexture;305class VKBuffer;306class VKSamplerState;307308enum {309MAX_BOUND_TEXTURES = MAX_TEXTURE_SLOTS,310};311312struct DescriptorSetKey {313VkImageView imageViews_[MAX_BOUND_TEXTURES];314VKSamplerState *samplers_[MAX_BOUND_TEXTURES];315VkBuffer buffer_;316317bool operator < (const DescriptorSetKey &other) const {318for (int i = 0; i < MAX_BOUND_TEXTURES; ++i) {319if (imageViews_[i] < other.imageViews_[i]) return true; else if (imageViews_[i] > other.imageViews_[i]) return false;320if (samplers_[i] < other.samplers_[i]) return true; else if (samplers_[i] > other.samplers_[i]) return false;321}322if (buffer_ < other.buffer_) return true; else if (buffer_ > other.buffer_) return false;323return false;324}325};326327class VKTexture : public Texture {328public:329VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc)330: vulkan_(vulkan), mipLevels_(desc.mipLevels) {331format_ = desc.format;332}333bool Create(VkCommandBuffer cmd, VulkanBarrierBatch *postBarriers, VulkanPushPool *pushBuffer, const TextureDesc &desc);334void Update(VkCommandBuffer cmd, VulkanBarrierBatch *postBarriers, VulkanPushPool *pushBuffer, const uint8_t *const *data, TextureCallback callback, int numLevels);335336~VKTexture() {337Destroy();338}339340VkImageView GetImageView() {341if (vkTex_) {342return vkTex_->GetImageView();343}344return VK_NULL_HANDLE; // This would be bad.345}346347VkImageView GetImageArrayView() {348if (vkTex_) {349return vkTex_->GetImageArrayView();350}351return VK_NULL_HANDLE; // This would be bad.352}353354int NumLevels() const {355return mipLevels_;356}357358private:359void UpdateInternal(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t *const *data, TextureCallback callback, int numLevels);360361void Destroy() {362if (vkTex_) {363vkTex_->Destroy();364delete vkTex_;365vkTex_ = nullptr;366}367}368369VulkanContext *vulkan_;370VulkanTexture *vkTex_ = nullptr;371372int mipLevels_ = 0;373};374375class VKFramebuffer;376377class VKContext : public DrawContext {378public:379VKContext(VulkanContext *vulkan, bool useRenderThread);380~VKContext();381382BackendState GetCurrentBackendState() const override {383return BackendState{384(u32)renderManager_.GetNumSteps(),385true, // Means that the other value is meaningful.386};387}388389void DebugAnnotate(const char *annotation) override;390void Wait() override {391vkDeviceWaitIdle(vulkan_->GetDevice());392}393394const DeviceCaps &GetDeviceCaps() const override {395return caps_;396}397std::vector<std::string> GetDeviceList() const override {398std::vector<std::string> list;399for (int i = 0; i < vulkan_->GetNumPhysicalDevices(); i++) {400list.push_back(vulkan_->GetPhysicalDeviceProperties(i).properties.deviceName);401}402return list;403}404std::vector<std::string> GetPresentModeList(std::string_view currentMarkerString) const override {405std::vector<std::string> list;406for (auto mode : vulkan_->GetAvailablePresentModes()) {407std::string str = VulkanPresentModeToString(mode);408if (mode == vulkan_->GetPresentMode()) {409str += std::string(" (") + std::string(currentMarkerString) + ")";410}411list.push_back(str);412}413return list;414}415std::vector<std::string> GetSurfaceFormatList() const override {416std::vector<std::string> list;417for (auto &format : vulkan_->SurfaceFormats()) {418std::string str = StringFromFormat("%s : %s", VulkanFormatToString(format.format), VulkanColorSpaceToString(format.colorSpace));419list.push_back(str);420}421return list;422}423424uint32_t GetSupportedShaderLanguages() const override {425return (uint32_t)ShaderLanguage::GLSL_VULKAN;426}427uint32_t GetDataFormatSupport(DataFormat fmt) const override;428429PresentMode GetPresentMode() const {430switch (vulkan_->GetPresentMode()) {431case VK_PRESENT_MODE_FIFO_KHR: return PresentMode::FIFO;432case VK_PRESENT_MODE_FIFO_RELAXED_KHR: return PresentMode::FIFO; // We treat is as FIFO for now (and won't ever enable it anyway...)433case VK_PRESENT_MODE_IMMEDIATE_KHR: return PresentMode::IMMEDIATE;434case VK_PRESENT_MODE_MAILBOX_KHR: return PresentMode::MAILBOX;435default: return PresentMode::FIFO;436}437}438439DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;440BlendState *CreateBlendState(const BlendStateDesc &desc) override;441InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;442SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;443RasterState *CreateRasterState(const RasterStateDesc &desc) override;444Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;445ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;446447Texture *CreateTexture(const TextureDesc &desc) override;448Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;449Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;450451void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;452void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override;453454void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override;455bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;456bool CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) override;457DataFormat PreferredFramebufferReadbackFormat(Framebuffer *src) override;458459// These functions should be self explanatory.460void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;461void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;462463void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;464465void SetScissorRect(int left, int top, int width, int height) override;466void SetViewport(const Viewport &viewport) override;467void SetBlendFactor(float color[4]) override;468void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;469470void BindSamplerStates(int start, int count, SamplerState **state) override;471void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;472void BindNativeTexture(int sampler, void *nativeTexture) override;473474void BindPipeline(Pipeline *pipeline) override {475curPipeline_ = (VKPipeline *)pipeline;476}477478void BindVertexBuffer(Buffer *vertexBuffer, int offset) override {479curVBuffer_ = (VKBuffer *)vertexBuffer;480curVBufferOffset_ = offset;481}482void BindIndexBuffer(Buffer *indexBuffer, int offset) override {483curIBuffer_ = (VKBuffer *)indexBuffer;484curIBufferOffset_ = offset;485}486487void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;488489// TODO: Add more sophisticated draws.490void Draw(int vertexCount, int offset) override;491void DrawIndexed(int vertexCount, int offset) override;492void DrawUP(const void *vdata, int vertexCount) override;493494void BindCurrentPipeline();495void ApplyDynamicState();496497void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;498499void BeginFrame(DebugFlags debugFlags) override;500void EndFrame() override;501void Present(PresentMode presentMode, int vblanks) override;502503int GetFrameCount() override {504return frameCount_;505}506507void FlushState() override {}508509void ResetStats() override {510renderManager_.ResetStats();511}512void StopThreads() override {513renderManager_.StopThreads();514}515516void StartThreads() override {517renderManager_.StartThreads();518}519520521std::string GetInfoString(InfoField info) const override {522// TODO: Make these actually query the right information523switch (info) {524case InfoField::APINAME: return "Vulkan";525case InfoField::VENDORSTRING: return vulkan_->GetPhysicalDeviceProperties().properties.deviceName;526case InfoField::VENDOR: return VulkanVendorString(vulkan_->GetPhysicalDeviceProperties().properties.vendorID);527case InfoField::DRIVER: return FormatDriverVersion(vulkan_->GetPhysicalDeviceProperties().properties);528case InfoField::SHADELANGVERSION: return "N/A";;529case InfoField::APIVERSION: return FormatAPIVersion(vulkan_->InstanceApiVersion());530case InfoField::DEVICE_API_VERSION: return FormatAPIVersion(vulkan_->DeviceApiVersion());531default: return "?";532}533}534535void BindDescriptors(VkBuffer buffer, PackedDescriptor descriptors[4]);536537std::vector<std::string> GetFeatureList() const override;538std::vector<std::string> GetExtensionList(bool device, bool enabledOnly) const override;539540uint64_t GetNativeObject(NativeObject obj, void *srcObject) override;541542void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;543544void Invalidate(InvalidationFlags flags) override;545546void InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channels) override;547548void SetInvalidationCallback(InvalidationCallback callback) override {549renderManager_.SetInvalidationCallback(callback);550}551552std::string GetGpuProfileString() const override {553return renderManager_.GetGpuProfileString();554}555556private:557VulkanTexture *GetNullTexture();558VulkanContext *vulkan_ = nullptr;559560int frameCount_ = 0;561VulkanRenderManager renderManager_;562563VulkanTexture *nullTexture_ = nullptr;564565AutoRef<VKPipeline> curPipeline_;566AutoRef<VKBuffer> curVBuffer_;567int curVBufferOffset_ = 0;568AutoRef<VKBuffer> curIBuffer_;569int curIBufferOffset_ = 0;570571VKRPipelineLayout *pipelineLayout_ = nullptr;572VkPipelineCache pipelineCache_ = VK_NULL_HANDLE;573AutoRef<VKFramebuffer> curFramebuffer_;574575VkDevice device_;576577enum {578MAX_FRAME_COMMAND_BUFFERS = 256,579};580AutoRef<VKTexture> boundTextures_[MAX_BOUND_TEXTURES];581AutoRef<VKSamplerState> boundSamplers_[MAX_BOUND_TEXTURES];582VkImageView boundImageView_[MAX_BOUND_TEXTURES]{};583TextureBindFlags boundTextureFlags_[MAX_BOUND_TEXTURES]{};584585VulkanPushPool *push_ = nullptr;586587DeviceCaps caps_{};588589uint8_t stencilRef_ = 0;590uint8_t stencilWriteMask_ = 0xFF;591uint8_t stencilCompareMask_ = 0xFF;592};593594// Bits per pixel, not bytes.595static int GetBpp(VkFormat format) {596switch (format) {597case VK_FORMAT_R8G8B8A8_UNORM:598case VK_FORMAT_B8G8R8A8_UNORM:599return 32;600case VK_FORMAT_R8_UNORM:601return 8;602case VK_FORMAT_R8G8_UNORM:603case VK_FORMAT_R16_UNORM:604return 16;605case VK_FORMAT_R4G4B4A4_UNORM_PACK16:606case VK_FORMAT_B4G4R4A4_UNORM_PACK16:607case VK_FORMAT_R5G5B5A1_UNORM_PACK16:608case VK_FORMAT_R5G6B5_UNORM_PACK16:609case VK_FORMAT_B5G5R5A1_UNORM_PACK16:610case VK_FORMAT_B5G6R5_UNORM_PACK16:611case VK_FORMAT_A1R5G5B5_UNORM_PACK16:612return 16;613case VK_FORMAT_D24_UNORM_S8_UINT:614return 32;615case VK_FORMAT_D16_UNORM:616return 16;617case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:618return 4;619case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:620case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:621return 8;622case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:623return 8;624case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:625return 4;626case VK_FORMAT_BC2_UNORM_BLOCK:627case VK_FORMAT_BC3_UNORM_BLOCK:628case VK_FORMAT_BC4_UNORM_BLOCK:629case VK_FORMAT_BC5_UNORM_BLOCK:630case VK_FORMAT_BC7_UNORM_BLOCK:631return 8;632default:633return 0;634}635}636637static VkFormat DataFormatToVulkan(DataFormat format) {638switch (format) {639case DataFormat::D16: return VK_FORMAT_D16_UNORM;640case DataFormat::D16_S8: return VK_FORMAT_D16_UNORM_S8_UINT;641case DataFormat::D24_S8: return VK_FORMAT_D24_UNORM_S8_UINT;642case DataFormat::D32F: return VK_FORMAT_D32_SFLOAT;643case DataFormat::D32F_S8: return VK_FORMAT_D32_SFLOAT_S8_UINT;644case DataFormat::S8: return VK_FORMAT_S8_UINT;645646case DataFormat::R16_UNORM: return VK_FORMAT_R16_UNORM;647648case DataFormat::R16_FLOAT: return VK_FORMAT_R16_SFLOAT;649case DataFormat::R16G16_FLOAT: return VK_FORMAT_R16G16_SFLOAT;650case DataFormat::R16G16B16A16_FLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT;651case DataFormat::R8_UNORM: return VK_FORMAT_R8_UNORM;652case DataFormat::R8G8_UNORM: return VK_FORMAT_R8G8_UNORM;653case DataFormat::R8G8B8_UNORM: return VK_FORMAT_R8G8B8_UNORM;654case DataFormat::R8G8B8A8_UNORM: return VK_FORMAT_R8G8B8A8_UNORM;655case DataFormat::R4G4_UNORM_PACK8: return VK_FORMAT_R4G4_UNORM_PACK8;656657// Note: A4R4G4B4_UNORM_PACK16 is not supported.658case DataFormat::R4G4B4A4_UNORM_PACK16: return VK_FORMAT_R4G4B4A4_UNORM_PACK16;659case DataFormat::B4G4R4A4_UNORM_PACK16: return VK_FORMAT_B4G4R4A4_UNORM_PACK16;660case DataFormat::R5G5B5A1_UNORM_PACK16: return VK_FORMAT_R5G5B5A1_UNORM_PACK16;661case DataFormat::B5G5R5A1_UNORM_PACK16: return VK_FORMAT_B5G5R5A1_UNORM_PACK16;662case DataFormat::R5G6B5_UNORM_PACK16: return VK_FORMAT_R5G6B5_UNORM_PACK16;663case DataFormat::B5G6R5_UNORM_PACK16: return VK_FORMAT_B5G6R5_UNORM_PACK16;664case DataFormat::A1R5G5B5_UNORM_PACK16: return VK_FORMAT_A1R5G5B5_UNORM_PACK16;665666case DataFormat::R32_FLOAT: return VK_FORMAT_R32_SFLOAT;667case DataFormat::R32G32_FLOAT: return VK_FORMAT_R32G32_SFLOAT;668case DataFormat::R32G32B32_FLOAT: return VK_FORMAT_R32G32B32_SFLOAT;669case DataFormat::R32G32B32A32_FLOAT: return VK_FORMAT_R32G32B32A32_SFLOAT;670671case DataFormat::BC1_RGBA_UNORM_BLOCK: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;672case DataFormat::BC2_UNORM_BLOCK: return VK_FORMAT_BC2_UNORM_BLOCK;673case DataFormat::BC3_UNORM_BLOCK: return VK_FORMAT_BC3_UNORM_BLOCK;674case DataFormat::BC4_UNORM_BLOCK: return VK_FORMAT_BC4_UNORM_BLOCK;675case DataFormat::BC5_UNORM_BLOCK: return VK_FORMAT_BC5_UNORM_BLOCK;676case DataFormat::BC7_UNORM_BLOCK: return VK_FORMAT_BC7_UNORM_BLOCK;677678case DataFormat::ETC2_R8G8B8A1_UNORM_BLOCK: return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;679case DataFormat::ETC2_R8G8B8A8_UNORM_BLOCK: return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;680case DataFormat::ETC2_R8G8B8_UNORM_BLOCK: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;681682case DataFormat::ASTC_4x4_UNORM_BLOCK: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;683684default:685return VK_FORMAT_UNDEFINED;686}687}688689static inline VkSamplerAddressMode AddressModeToVulkan(Draw::TextureAddressMode mode) {690switch (mode) {691case TextureAddressMode::CLAMP_TO_BORDER: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;692case TextureAddressMode::CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;693case TextureAddressMode::REPEAT_MIRROR: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;694default:695case TextureAddressMode::REPEAT: return VK_SAMPLER_ADDRESS_MODE_REPEAT;696}697}698699VulkanTexture *VKContext::GetNullTexture() {700if (!nullTexture_) {701VkCommandBuffer cmdInit = renderManager_.GetInitCmd();702nullTexture_ = new VulkanTexture(vulkan_, "Null");703int w = 8;704int h = 8;705VulkanBarrierBatch barrier;706nullTexture_->CreateDirect(w, h, 1, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,707VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, &barrier);708barrier.Flush(cmdInit);709uint32_t bindOffset;710VkBuffer bindBuf;711uint32_t *data = (uint32_t *)push_->Allocate(w * h * 4, 4, &bindBuf, &bindOffset);712_assert_(data != nullptr);713for (int y = 0; y < h; y++) {714for (int x = 0; x < w; x++) {715// data[y*w + x] = ((x ^ y) & 1) ? 0xFF808080 : 0xFF000000; // gray/black checkerboard716data[y*w + x] = 0; // black717}718}719TextureCopyBatch batch;720nullTexture_->CopyBufferToMipLevel(cmdInit, &batch, 0, w, h, 0, bindBuf, bindOffset, w);721nullTexture_->FinishCopyBatch(cmdInit, &batch);722nullTexture_->EndCreate(cmdInit, false, VK_PIPELINE_STAGE_TRANSFER_BIT);723}724return nullTexture_;725}726727class VKSamplerState : public SamplerState {728public:729VKSamplerState(VulkanContext *vulkan, const SamplerStateDesc &desc) : vulkan_(vulkan) {730VkSamplerCreateInfo s = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };731s.addressModeU = AddressModeToVulkan(desc.wrapU);732s.addressModeV = AddressModeToVulkan(desc.wrapV);733s.addressModeW = AddressModeToVulkan(desc.wrapW);734s.anisotropyEnable = desc.maxAniso > 1.0f;735s.maxAnisotropy = desc.maxAniso;736s.magFilter = desc.magFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;737s.minFilter = desc.minFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;738s.mipmapMode = desc.mipFilter == TextureFilter::LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;739s.maxLod = VK_LOD_CLAMP_NONE;740VkResult res = vkCreateSampler(vulkan_->GetDevice(), &s, nullptr, &sampler_);741_assert_(VK_SUCCESS == res);742}743~VKSamplerState() {744vulkan_->Delete().QueueDeleteSampler(sampler_);745}746747VkSampler GetSampler() { return sampler_; }748749private:750VulkanContext *vulkan_;751VkSampler sampler_;752};753754SamplerState *VKContext::CreateSamplerState(const SamplerStateDesc &desc) {755return new VKSamplerState(vulkan_, desc);756}757758RasterState *VKContext::CreateRasterState(const RasterStateDesc &desc) {759return new VKRasterState(vulkan_, desc);760}761762void VKContext::BindSamplerStates(int start, int count, SamplerState **state) {763_assert_(start + count <= MAX_BOUND_TEXTURES);764for (int i = start; i < start + count; i++) {765boundSamplers_[i] = (VKSamplerState *)state[i - start];766}767}768769enum class TextureState {770UNINITIALIZED,771STAGED,772INITIALIZED,773PENDING_DESTRUCTION,774};775776bool VKTexture::Create(VkCommandBuffer cmd, VulkanBarrierBatch *postBarriers, VulkanPushPool *pushBuffer, const TextureDesc &desc) {777// Zero-sized textures not allowed.778_assert_(desc.width * desc.height * desc.depth > 0); // remember to set depth to 1!779if (desc.width * desc.height * desc.depth <= 0) {780ERROR_LOG(Log::G3D, "Bad texture dimensions %dx%dx%d", desc.width, desc.height, desc.depth);781return false;782}783_dbg_assert_(pushBuffer);784format_ = desc.format;785mipLevels_ = desc.mipLevels;786width_ = desc.width;787height_ = desc.height;788depth_ = desc.depth;789vkTex_ = new VulkanTexture(vulkan_, desc.tag);790VkFormat vulkanFormat = DataFormatToVulkan(format_);791int usageBits = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;792if (mipLevels_ > (int)desc.initData.size()) {793// Gonna have to generate some, which requires TRANSFER_SRC794usageBits |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;795}796797VkComponentMapping r8AsAlpha[4] = { VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_R };798799VulkanBarrierBatch barrier;800if (!vkTex_->CreateDirect(width_, height_, 1, mipLevels_, vulkanFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, usageBits, &barrier, desc.swizzle == TextureSwizzle::R8_AS_ALPHA ? r8AsAlpha : nullptr)) {801ERROR_LOG(Log::G3D, "Failed to create VulkanTexture: %dx%dx%d fmt %d, %d levels", width_, height_, depth_, (int)vulkanFormat, mipLevels_);802return false;803}804barrier.Flush(cmd);805VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;806if (desc.initData.size()) {807UpdateInternal(cmd, pushBuffer, desc.initData.data(), desc.initDataCallback, (int)desc.initData.size());808// Generate the rest of the mips automatically.809if (desc.initData.size() < mipLevels_) {810vkTex_->GenerateMips(cmd, (int)desc.initData.size(), false);811layout = VK_IMAGE_LAYOUT_GENERAL;812}813}814vkTex_->EndCreate(cmd, false, VK_PIPELINE_STAGE_TRANSFER_BIT, layout);815return true;816}817818void VKTexture::Update(VkCommandBuffer cmd, VulkanBarrierBatch *postBarriers, VulkanPushPool *pushBuffer, const uint8_t * const *data, TextureCallback initDataCallback, int numLevels) {819// Before we can use UpdateInternal, we need to transition the image to the same state as after CreateDirect,820// making it ready for writing.821vkTex_->PrepareForTransferDst(cmd, numLevels);822UpdateInternal(cmd, pushBuffer, data, initDataCallback, numLevels);823vkTex_->RestoreAfterTransferDst(numLevels, postBarriers);824}825826void VKTexture::UpdateInternal(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t * const *data, TextureCallback initDataCallback, int numLevels) {827int w = width_;828int h = height_;829int d = depth_;830VkFormat vulkanFormat = DataFormatToVulkan(format_);831int bpp = GetBpp(vulkanFormat);832int bytesPerPixel = bpp / 8;833TextureCopyBatch batch;834batch.reserve(numLevels);835for (int i = 0; i < numLevels; i++) {836uint32_t offset;837VkBuffer buf;838size_t size = w * h * d * bytesPerPixel;839uint8_t *dest = (uint8_t *)pushBuffer->Allocate(size, 16, &buf, &offset);840if (initDataCallback) {841_assert_(dest != nullptr);842if (!initDataCallback(dest, data[i], w, h, d, w * bytesPerPixel, h * w * bytesPerPixel)) {843memcpy(dest, data[i], size);844}845} else {846memcpy(dest, data[i], size);847}848vkTex_->CopyBufferToMipLevel(cmd, &batch, i, w, h, 0, buf, offset, w);849w = (w + 1) / 2;850h = (h + 1) / 2;851d = (d + 1) / 2;852}853vkTex_->FinishCopyBatch(cmd, &batch);854}855856static DataFormat DataFormatFromVulkanDepth(VkFormat fmt) {857switch (fmt) {858case VK_FORMAT_D24_UNORM_S8_UINT:859return DataFormat::D24_S8;860case VK_FORMAT_D16_UNORM:861return DataFormat::D16;862case VK_FORMAT_D32_SFLOAT:863return DataFormat::D32F;864case VK_FORMAT_D32_SFLOAT_S8_UINT:865return DataFormat::D32F_S8;866case VK_FORMAT_D16_UNORM_S8_UINT:867return DataFormat::D16_S8;868default:869break;870}871872return DataFormat::UNDEFINED;873}874875VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread)876: vulkan_(vulkan), renderManager_(vulkan, useRenderThread, frameTimeHistory_) {877shaderLanguageDesc_.Init(GLSL_VULKAN);878879VkFormat depthStencilFormat = vulkan->GetDeviceInfo().preferredDepthStencilFormat;880881INFO_LOG(Log::G3D, "Determining Vulkan device caps");882883caps_.setMaxFrameLatencySupported = true;884caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.standard.samplerAnisotropy != 0;885caps_.geometryShaderSupported = vulkan->GetDeviceFeatures().enabled.standard.geometryShader != 0;886caps_.tesselationShaderSupported = vulkan->GetDeviceFeatures().enabled.standard.tessellationShader != 0;887caps_.dualSourceBlend = vulkan->GetDeviceFeatures().enabled.standard.dualSrcBlend != 0;888caps_.depthClampSupported = vulkan->GetDeviceFeatures().enabled.standard.depthClamp != 0;889890// Comment out these two to test geometry shader culling on any geometry shader-supporting hardware.891caps_.clipDistanceSupported = vulkan->GetDeviceFeatures().enabled.standard.shaderClipDistance != 0;892caps_.cullDistanceSupported = vulkan->GetDeviceFeatures().enabled.standard.shaderCullDistance != 0;893894caps_.framebufferBlitSupported = true;895caps_.framebufferCopySupported = true;896caps_.framebufferDepthBlitSupported = vulkan->GetDeviceInfo().canBlitToPreferredDepthStencilFormat;897caps_.framebufferStencilBlitSupported = caps_.framebufferDepthBlitSupported;898caps_.framebufferDepthCopySupported = true; // Will pretty much always be the case.899caps_.framebufferSeparateDepthCopySupported = true; // Will pretty much always be the case.900// This doesn't affect what depth/stencil format is actually used, see VulkanQueueRunner.901caps_.preferredDepthBufferFormat = DataFormatFromVulkanDepth(vulkan->GetDeviceInfo().preferredDepthStencilFormat);902caps_.texture3DSupported = true;903caps_.textureDepthSupported = true;904caps_.fragmentShaderInt32Supported = true;905caps_.textureNPOTFullySupported = true;906caps_.fragmentShaderDepthWriteSupported = true;907caps_.fragmentShaderStencilWriteSupported = vulkan->Extensions().EXT_shader_stencil_export;908caps_.blendMinMaxSupported = true;909caps_.logicOpSupported = vulkan->GetDeviceFeatures().enabled.standard.logicOp != 0;910caps_.multiViewSupported = vulkan->GetDeviceFeatures().enabled.multiview.multiview != 0;911caps_.sampleRateShadingSupported = vulkan->GetDeviceFeatures().enabled.standard.sampleRateShading != 0;912caps_.textureSwizzleSupported = true;913914// Note that it must also be enabled on the pipelines (which we do).915caps_.provokingVertexLast = vulkan->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast;916917// Present mode stuff918caps_.presentMaxInterval = 1;919caps_.presentInstantModeChange = false; // TODO: Fix this with some work in VulkanContext920caps_.presentModesSupported = (PresentMode)0;921for (auto mode : vulkan->GetAvailablePresentModes()) {922switch (mode) {923case VK_PRESENT_MODE_FIFO_KHR: caps_.presentModesSupported |= PresentMode::FIFO; break;924case VK_PRESENT_MODE_IMMEDIATE_KHR: caps_.presentModesSupported |= PresentMode::IMMEDIATE; break;925case VK_PRESENT_MODE_MAILBOX_KHR: caps_.presentModesSupported |= PresentMode::MAILBOX; break;926default: break; // Ignore any other modes.927}928}929930const auto &limits = vulkan->GetPhysicalDeviceProperties().properties.limits;931932auto deviceProps = vulkan->GetPhysicalDeviceProperties(vulkan_->GetCurrentPhysicalDeviceIndex()).properties;933934switch (deviceProps.vendorID) {935case VULKAN_VENDOR_AMD: caps_.vendor = GPUVendor::VENDOR_AMD; break;936case VULKAN_VENDOR_ARM: caps_.vendor = GPUVendor::VENDOR_ARM; break;937case VULKAN_VENDOR_IMGTEC: caps_.vendor = GPUVendor::VENDOR_IMGTEC; break;938case VULKAN_VENDOR_NVIDIA: caps_.vendor = GPUVendor::VENDOR_NVIDIA; break;939case VULKAN_VENDOR_QUALCOMM: caps_.vendor = GPUVendor::VENDOR_QUALCOMM; break;940case VULKAN_VENDOR_INTEL: caps_.vendor = GPUVendor::VENDOR_INTEL; break;941case VULKAN_VENDOR_APPLE: caps_.vendor = GPUVendor::VENDOR_APPLE; break;942case VULKAN_VENDOR_MESA: caps_.vendor = GPUVendor::VENDOR_MESA; break;943default:944WARN_LOG(Log::G3D, "Unknown vendor ID %08x", deviceProps.vendorID);945caps_.vendor = GPUVendor::VENDOR_UNKNOWN;946break;947}948949switch (caps_.vendor) {950case GPUVendor::VENDOR_ARM:951case GPUVendor::VENDOR_IMGTEC:952case GPUVendor::VENDOR_QUALCOMM:953caps_.isTilingGPU = true;954break;955default:956caps_.isTilingGPU = false;957break;958}959960if (caps_.vendor == GPUVendor::VENDOR_IMGTEC) {961// Enable some things that cut down pipeline counts but may have other costs.962caps_.verySlowShaderCompiler = true;963}964965// Hide D3D9 when we know it likely won't work well.966#if PPSSPP_PLATFORM(WINDOWS)967caps_.supportsD3D9 = true;968if (!strcmp(deviceProps.deviceName, "Intel(R) Iris(R) Xe Graphics")) {969caps_.supportsD3D9 = false;970}971#endif972973// VkSampleCountFlagBits is arranged correctly for our purposes.974// Only support MSAA levels that have support for all three of color, depth, stencil.975976bool multisampleAllowed = true;977978caps_.deviceID = deviceProps.deviceID;979980if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) {981if (caps_.deviceID < 0x6000000) { // On sub 6xx series GPUs, disallow multisample.982INFO_LOG(Log::G3D, "Multisampling was disabled due to old driver version (Adreno)");983multisampleAllowed = false;984}985986// Adreno 5xx devices, all known driver versions, fail to discard stencil when depth write is off.987// See: https://github.com/hrydgard/ppsspp/pull/11684988if (deviceProps.deviceID >= 0x05000000 && deviceProps.deviceID < 0x06000000) {989if (deviceProps.driverVersion < 0x80180000) {990bugs_.Infest(Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL_ADRENO);991}992}993// Color write mask not masking write in certain scenarios with a depth test, see #10421.994// Known still present on driver 0x80180000 and Adreno 5xx (possibly more.)995// Known working on driver 0x801EA000 and Adreno 620.996if (deviceProps.driverVersion < 0x801EA000 || deviceProps.deviceID < 0x06000000)997bugs_.Infest(Bugs::COLORWRITEMASK_BROKEN_WITH_DEPTHTEST);998999// Trying to follow all the rules in https://registry.khronos.org/vulkan/specs/1.3/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies1000// and https://registry.khronos.org/vulkan/specs/1.3/html/vkspec.html#renderpass-feedbackloop, but still it doesn't1001// quite work - artifacts on triangle boundaries on Adreno.1002bugs_.Infest(Bugs::SUBPASS_FEEDBACK_BROKEN);1003} else if (caps_.vendor == GPUVendor::VENDOR_AMD) {1004// See issue #10074, and also #10065 (AMD) and #10109 for the choice of the driver version to check for.1005if (deviceProps.driverVersion < 0x00407000) {1006bugs_.Infest(Bugs::DUAL_SOURCE_BLENDING_BROKEN);1007}1008} else if (caps_.vendor == GPUVendor::VENDOR_INTEL) {1009// Workaround for Intel driver bug. TODO: Re-enable after some driver version1010bugs_.Infest(Bugs::DUAL_SOURCE_BLENDING_BROKEN);1011} else if (caps_.vendor == GPUVendor::VENDOR_ARM) {1012// Really old Vulkan drivers for Mali didn't have proper versions. We try to detect that (can't be 100% but pretty good).1013bool isOldVersion = IsHashMaliDriverVersion(deviceProps);10141015int majorVersion = VK_API_VERSION_MAJOR(deviceProps.driverVersion);10161017// These GPUs (up to some certain hardware version?) have a bug where draws where gl_Position.w == .z1018// corrupt the depth buffer. This is easily worked around by simply scaling Z down a tiny bit when this case1019// is detected. See: https://github.com/hrydgard/ppsspp/issues/119371020bugs_.Infest(Bugs::EQUAL_WZ_CORRUPTS_DEPTH);10211022// Nearly identical to the the Adreno bug, see #13833 (Midnight Club map broken) and other issues.1023// It has the additional caveat that combining depth writes with NEVER depth tests crashes the driver.1024// Reported fixed in major version 40 - let's add a check once confirmed.1025bugs_.Infest(Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL_MALI);10261027// This started in driver 31 or 32, fixed in 40 - let's add a check once confirmed.1028if (majorVersion >= 32) {1029bugs_.Infest(Bugs::MALI_CONSTANT_LOAD_BUG); // See issue #156611030}10311032// Older ARM devices have very slow geometry shaders, not worth using. At least before 15.1033// Also seen to cause weird issues on 18, so let's lump it in.1034if (majorVersion <= 18 || isOldVersion) {1035bugs_.Infest(Bugs::GEOMETRY_SHADERS_SLOW_OR_BROKEN);1036}10371038// Attempt to workaround #173861039if (isOldVersion) {1040if (!strcmp(deviceProps.deviceName, "Mali-T880") ||1041!strcmp(deviceProps.deviceName, "Mali-T860") ||1042!strcmp(deviceProps.deviceName, "Mali-T830")) {1043bugs_.Infest(Bugs::UNIFORM_INDEXING_BROKEN);1044}1045}10461047if (isOldVersion) {1048// Very rough heuristic.1049multisampleAllowed = false;1050}1051} else if (caps_.vendor == GPUVendor::VENDOR_IMGTEC) {1052// Not sure about driver versions, so let's just ban, impact is tiny.1053bugs_.Infest(Bugs::PVR_BAD_16BIT_TEXFORMATS);1054}10551056if (!vulkan->Extensions().KHR_depth_stencil_resolve) {1057INFO_LOG(Log::G3D, "KHR_depth_stencil_resolve not supported, disabling multisampling");1058multisampleAllowed = false;1059}10601061if (!vulkan->Extensions().KHR_create_renderpass2) {1062WARN_LOG(Log::G3D, "KHR_create_renderpass2 not supported, disabling multisampling");1063multisampleAllowed = false;1064} else {1065_dbg_assert_(vkCreateRenderPass2 != nullptr);1066}10671068// We limit multisampling functionality to reasonably recent and known-good tiling GPUs.1069if (multisampleAllowed) {1070// Check for depth stencil resolve. Without it, depth textures won't work, and we don't want that mess1071// of compatibility reports, so we'll just disable multisampling in this case for now.1072// There are potential workarounds for devices that don't support it, but those are nearly non-existent now.1073const auto &resolveProperties = vulkan->GetPhysicalDeviceProperties().depthStencilResolve;1074if (((resolveProperties.supportedDepthResolveModes & resolveProperties.supportedStencilResolveModes) & VK_RESOLVE_MODE_SAMPLE_ZERO_BIT) != 0) {1075caps_.multiSampleLevelsMask = (limits.framebufferColorSampleCounts & limits.framebufferDepthSampleCounts & limits.framebufferStencilSampleCounts);1076INFO_LOG(Log::G3D, "Multisample levels mask: %d", caps_.multiSampleLevelsMask);1077} else {1078INFO_LOG(Log::G3D, "Not enough depth/stencil resolve modes supported, disabling multisampling. Color: %d Depth: %d Stencil: %d",1079limits.framebufferColorSampleCounts, limits.framebufferDepthSampleCounts, limits.framebufferStencilSampleCounts);1080caps_.multiSampleLevelsMask = 1;1081}1082} else {1083caps_.multiSampleLevelsMask = 1;1084}10851086// Vulkan can support this through input attachments and various extensions, but not worth1087// the trouble.1088caps_.framebufferFetchSupported = false;10891090device_ = vulkan->GetDevice();10911092VkBufferUsageFlags usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;1093push_ = new VulkanPushPool(vulkan_, "pushBuffer", 4 * 1024 * 1024, usage);10941095// binding 0 - uniform data1096// binding 1 - combined sampler/image 01097// binding 2 - combined sampler/image 11098// ...etc1099BindingType bindings[MAX_BOUND_TEXTURES + 1];1100bindings[0] = BindingType::UNIFORM_BUFFER_DYNAMIC_ALL;1101for (int i = 0; i < MAX_BOUND_TEXTURES; ++i) {1102bindings[1 + i] = BindingType::COMBINED_IMAGE_SAMPLER;1103}1104pipelineLayout_ = renderManager_.CreatePipelineLayout(bindings, ARRAY_SIZE(bindings), caps_.geometryShaderSupported, "thin3d_layout");11051106VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };1107VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);1108_assert_(VK_SUCCESS == res);1109}11101111VKContext::~VKContext() {1112DestroyPresets();11131114delete nullTexture_;1115push_->Destroy();1116delete push_;1117renderManager_.DestroyPipelineLayout(pipelineLayout_);1118vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);1119}11201121void VKContext::BeginFrame(DebugFlags debugFlags) {1122renderManager_.BeginFrame(debugFlags & DebugFlags::PROFILE_TIMESTAMPS, debugFlags & DebugFlags::PROFILE_SCOPES);1123push_->BeginFrame();1124}11251126void VKContext::EndFrame() {1127// Do all the work to submit the command buffers etc.1128renderManager_.Finish();1129// Unbind stuff, to avoid accidentally relying on it across frames (and provide some protection against forgotten unbinds of deleted things).1130Invalidate(InvalidationFlags::CACHED_RENDER_STATE);1131}11321133void VKContext::Present(PresentMode presentMode, int vblanks) {1134if (presentMode == PresentMode::FIFO) {1135_dbg_assert_(vblanks == 0 || vblanks == 1);1136}1137renderManager_.Present();1138frameCount_++;1139}11401141void VKContext::Invalidate(InvalidationFlags flags) {1142if (flags & InvalidationFlags::CACHED_RENDER_STATE) {1143curPipeline_ = nullptr;11441145for (auto &view : boundImageView_) {1146view = VK_NULL_HANDLE;1147}1148for (auto &sampler : boundSamplers_) {1149sampler = nullptr;1150}1151for (auto &texture : boundTextures_) {1152texture = nullptr;1153}1154}1155}11561157void VKContext::BindDescriptors(VkBuffer buf, PackedDescriptor descriptors[4]) {1158descriptors[0].buffer.buffer = buf;1159descriptors[0].buffer.offset = 0; // dynamic1160descriptors[0].buffer.range = curPipeline_->GetUBOSize();11611162int numDescs = 1;1163for (int i = 0; i < MAX_BOUND_TEXTURES; ++i) {1164VkImageView view;1165VkSampler sampler;1166if (boundTextures_[i]) {1167view = (boundTextureFlags_[i] & TextureBindFlags::VULKAN_BIND_ARRAY) ? boundTextures_[i]->GetImageArrayView() : boundTextures_[i]->GetImageView();1168} else {1169view = boundImageView_[i];1170}1171sampler = boundSamplers_[i] ? boundSamplers_[i]->GetSampler() : VK_NULL_HANDLE;11721173if (view && sampler) {1174descriptors[i + 1].image.view = view;1175descriptors[i + 1].image.sampler = sampler;1176} else {1177descriptors[i + 1].image.view = VK_NULL_HANDLE;1178descriptors[i + 1].image.sampler = VK_NULL_HANDLE;1179}1180}1181}11821183Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {1184VKInputLayout *input = (VKInputLayout *)desc.inputLayout;1185VKBlendState *blend = (VKBlendState *)desc.blend;1186VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil;1187VKRasterState *raster = (VKRasterState *)desc.raster;11881189PipelineFlags pipelineFlags = (PipelineFlags)0;1190if (depth->info.depthTestEnable || depth->info.stencilTestEnable) {1191pipelineFlags |= PipelineFlags::USES_DEPTH_STENCIL;1192}1193// TODO: We need code to set USES_BLEND_CONSTANT here too, if we're ever gonna use those in thin3d code.11941195VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), pipelineFlags, tag);11961197VKRGraphicsPipelineDesc &gDesc = *pipeline->vkrDesc;11981199std::vector<VkPipelineShaderStageCreateInfo> stages;1200stages.resize(desc.shaders.size());12011202for (auto &iter : desc.shaders) {1203VKShaderModule *vkshader = (VKShaderModule *)iter;1204vkshader->AddRef();1205pipeline->deps.push_back(vkshader);1206if (vkshader->GetStage() == ShaderStage::Vertex) {1207gDesc.vertexShader = vkshader->Get();1208} else if (vkshader->GetStage() == ShaderStage::Fragment) {1209gDesc.fragmentShader = vkshader->Get();1210} else {1211ERROR_LOG(Log::G3D, "Bad stage");1212delete pipeline;1213return nullptr;1214}1215}12161217_dbg_assert_(input);1218_dbg_assert_((int)input->attributes.size() == (int)input->visc.vertexAttributeDescriptionCount);12191220pipeline->stride = input->binding.stride;1221gDesc.ibd = input->binding;1222for (size_t i = 0; i < input->attributes.size(); i++) {1223gDesc.attrs[i] = input->attributes[i];1224}1225gDesc.vis.vertexAttributeDescriptionCount = input->visc.vertexAttributeDescriptionCount;1226gDesc.vis.vertexBindingDescriptionCount = input->visc.vertexBindingDescriptionCount;1227gDesc.vis.pVertexBindingDescriptions = &gDesc.ibd;1228gDesc.vis.pVertexAttributeDescriptions = gDesc.attrs;12291230gDesc.blend0 = blend->attachments[0];1231gDesc.cbs = blend->info;1232gDesc.cbs.pAttachments = &gDesc.blend0;12331234gDesc.dss = depth->info;12351236// Copy bindings from input layout.1237gDesc.topology = primToVK[(int)desc.prim];12381239// We treat the three stencil states as a unit in other places, so let's do that here too.1240const VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK };1241gDesc.ds.dynamicStateCount = depth->info.stencilTestEnable ? ARRAY_SIZE(dynamics) : 2;1242for (size_t i = 0; i < gDesc.ds.dynamicStateCount; i++) {1243gDesc.dynamicStates[i] = dynamics[i];1244}1245gDesc.ds.pDynamicStates = gDesc.dynamicStates;12461247gDesc.views.viewportCount = 1;1248gDesc.views.scissorCount = 1;1249gDesc.views.pViewports = nullptr; // dynamic1250gDesc.views.pScissors = nullptr; // dynamic12511252gDesc.pipelineLayout = pipelineLayout_;12531254VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };1255raster->ToVulkan(&gDesc.rs);12561257if (renderManager_.GetVulkanContext()->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast) {1258ChainStruct(gDesc.rs, &gDesc.rs_provoking);1259gDesc.rs_provoking.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;1260}12611262pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, pipelineFlags, 1 << (size_t)RenderPassType::BACKBUFFER, VK_SAMPLE_COUNT_1_BIT, false, tag ? tag : "thin3d");12631264if (desc.uniformDesc) {1265pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;1266}1267if (depth->info.stencilTestEnable) {1268pipeline->usesStencil = true;1269}1270return pipeline;1271}12721273void VKContext::SetScissorRect(int left, int top, int width, int height) {1274renderManager_.SetScissor(left, top, width, height);1275}12761277void VKContext::SetViewport(const Viewport &viewport) {1278// Ignore viewports more than the first.1279VkViewport vkViewport;1280vkViewport.x = viewport.TopLeftX;1281vkViewport.y = viewport.TopLeftY;1282vkViewport.width = viewport.Width;1283vkViewport.height = viewport.Height;1284vkViewport.minDepth = viewport.MinDepth;1285vkViewport.maxDepth = viewport.MaxDepth;1286renderManager_.SetViewport(vkViewport);1287}12881289void VKContext::SetBlendFactor(float color[4]) {1290uint32_t col = Float4ToUint8x4(color);1291renderManager_.SetBlendFactor(col);1292}12931294void VKContext::SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) {1295if (curPipeline_->usesStencil)1296renderManager_.SetStencilParams(writeMask, compareMask, refValue);1297stencilRef_ = refValue;1298stencilWriteMask_ = writeMask;1299stencilCompareMask_ = compareMask;1300}13011302InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) {1303VKInputLayout *vl = new VKInputLayout();1304vl->visc = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };1305vl->visc.flags = 0;1306vl->visc.vertexBindingDescriptionCount = 1;1307vl->visc.vertexAttributeDescriptionCount = (uint32_t)desc.attributes.size();1308vl->attributes.resize(vl->visc.vertexAttributeDescriptionCount);1309vl->visc.pVertexBindingDescriptions = &vl->binding;1310vl->visc.pVertexAttributeDescriptions = vl->attributes.data();1311for (size_t i = 0; i < desc.attributes.size(); i++) {1312vl->attributes[i].binding = 0;1313vl->attributes[i].format = DataFormatToVulkan(desc.attributes[i].format);1314vl->attributes[i].location = desc.attributes[i].location;1315vl->attributes[i].offset = desc.attributes[i].offset;1316}1317vl->binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;1318vl->binding.binding = 0;1319vl->binding.stride = desc.stride;1320return vl;1321}13221323Texture *VKContext::CreateTexture(const TextureDesc &desc) {1324VkCommandBuffer initCmd = renderManager_.GetInitCmd();1325if (!push_ || !initCmd) {1326// Too early! Fail.1327ERROR_LOG(Log::G3D, "Can't create textures before the first frame has started.");1328return nullptr;1329}1330VKTexture *tex = new VKTexture(vulkan_, initCmd, push_, desc);1331if (tex->Create(initCmd, &renderManager_.PostInitBarrier(), push_, desc)) {1332return tex;1333} else {1334ERROR_LOG(Log::G3D, "Failed to create texture");1335tex->Release();1336return nullptr;1337}1338}13391340void VKContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) {1341VkCommandBuffer initCmd = renderManager_.GetInitCmd();1342if (!push_ || !initCmd) {1343// Too early! Fail.1344ERROR_LOG(Log::G3D, "Can't create textures before the first frame has started.");1345return;1346}13471348VKTexture *tex = (VKTexture *)texture;13491350_dbg_assert_(numLevels <= tex->NumLevels());1351tex->Update(initCmd, &renderManager_.PostInitBarrier(), push_, data, initDataCallback, numLevels);1352}13531354static inline void CopySide(VkStencilOpState &dest, const StencilSetup &src) {1355dest.compareOp = compToVK[(int)src.compareOp];1356dest.failOp = stencilOpToVK[(int)src.failOp];1357dest.passOp = stencilOpToVK[(int)src.passOp];1358dest.depthFailOp = stencilOpToVK[(int)src.depthFailOp];1359}13601361DepthStencilState *VKContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {1362VKDepthStencilState *ds = new VKDepthStencilState();1363ds->info.depthCompareOp = compToVK[(int)desc.depthCompare];1364ds->info.depthTestEnable = desc.depthTestEnabled;1365ds->info.depthWriteEnable = desc.depthWriteEnabled;1366ds->info.stencilTestEnable = desc.stencilEnabled;1367ds->info.depthBoundsTestEnable = false;1368if (ds->info.stencilTestEnable) {1369CopySide(ds->info.front, desc.stencil);1370CopySide(ds->info.back, desc.stencil);1371}1372return ds;1373}13741375BlendState *VKContext::CreateBlendState(const BlendStateDesc &desc) {1376VKBlendState *bs = new VKBlendState();1377bs->info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;1378bs->info.attachmentCount = 1;1379bs->info.logicOp = logicOpToVK[(int)desc.logicOp];1380bs->info.logicOpEnable = desc.logicEnabled;1381bs->attachments.resize(1);1382bs->attachments[0].blendEnable = desc.enabled;1383bs->attachments[0].colorBlendOp = blendEqToVk[(int)desc.eqCol];1384bs->attachments[0].alphaBlendOp = blendEqToVk[(int)desc.eqAlpha];1385bs->attachments[0].colorWriteMask = desc.colorMask;1386bs->attachments[0].dstAlphaBlendFactor = blendFactorToVk[(int)desc.dstAlpha];1387bs->attachments[0].dstColorBlendFactor = blendFactorToVk[(int)desc.dstCol];1388bs->attachments[0].srcAlphaBlendFactor = blendFactorToVk[(int)desc.srcAlpha];1389bs->attachments[0].srcColorBlendFactor = blendFactorToVk[(int)desc.srcCol];1390bs->info.pAttachments = bs->attachments.data();1391return bs;1392}13931394// Very simplistic buffer that will simply copy its contents into our "pushbuffer" when it's time to draw,1395// to avoid synchronization issues.1396class VKBuffer : public Buffer {1397public:1398VKBuffer(size_t size, uint32_t flags) : dataSize_(size) {1399data_ = new uint8_t[size];1400}1401~VKBuffer() {1402delete[] data_;1403}14041405size_t GetSize() const { return dataSize_; }1406const uint8_t *GetData() const { return data_; }14071408uint8_t *data_;1409size_t dataSize_;1410};14111412Buffer *VKContext::CreateBuffer(size_t size, uint32_t usageFlags) {1413return new VKBuffer(size, usageFlags);1414}14151416void VKContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) {1417VKBuffer *buf = (VKBuffer *)buffer;1418memcpy(buf->data_ + offset, data, size);1419}14201421void VKContext::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {1422_assert_(start + count <= MAX_BOUND_TEXTURES);1423for (int i = start; i < start + count; i++) {1424_dbg_assert_(i >= 0 && i < MAX_BOUND_TEXTURES);1425boundTextures_[i] = static_cast<VKTexture *>(textures[i - start]);1426boundTextureFlags_[i] = flags;1427if (boundTextures_[i]) {1428// If a texture is bound, we set these up in BindDescriptors too.1429// But we might need to set the view here anyway so it can be queried using GetNativeObject.1430if (flags & TextureBindFlags::VULKAN_BIND_ARRAY) {1431boundImageView_[i] = boundTextures_[i]->GetImageArrayView();1432} else {1433boundImageView_[i] = boundTextures_[i]->GetImageView();1434}1435} else {1436if (flags & TextureBindFlags::VULKAN_BIND_ARRAY) {1437boundImageView_[i] = GetNullTexture()->GetImageArrayView();1438} else {1439boundImageView_[i] = GetNullTexture()->GetImageView();1440}1441}1442}1443}14441445void VKContext::BindNativeTexture(int sampler, void *nativeTexture) {1446_dbg_assert_(sampler >= 0 && sampler < MAX_BOUND_TEXTURES);1447boundTextures_[sampler] = nullptr;1448boundImageView_[sampler] = (VkImageView)nativeTexture;1449}14501451ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) {1452VKShaderModule *shader = new VKShaderModule(stage, tag);1453if (shader->Compile(vulkan_, language, data, size)) {1454return shader;1455} else {1456ERROR_LOG(Log::G3D, "Failed to compile shader %s:\n%s", tag, (const char *)LineNumberString((const char *)data).c_str());1457shader->Release();1458return nullptr;1459}1460}14611462void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {1463curPipeline_->SetDynamicUniformData(ub, size);1464}14651466void VKContext::ApplyDynamicState() {1467// TODO: blend constants, stencil, viewports should be here, after bindpipeline..1468if (curPipeline_->usesStencil) {1469renderManager_.SetStencilParams(stencilWriteMask_, stencilCompareMask_, stencilRef_);1470}1471}14721473void VKContext::Draw(int vertexCount, int offset) {1474VKBuffer *vbuf = curVBuffer_;14751476VkBuffer vulkanVbuf;1477VkBuffer vulkanUBObuf;1478uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);1479size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), 4, &vulkanVbuf);14801481BindCurrentPipeline();1482ApplyDynamicState();1483int descSetIndex;1484PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex);1485BindDescriptors(vulkanUBObuf, descriptors);1486renderManager_.Draw(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vertexCount, offset);1487}14881489void VKContext::DrawIndexed(int vertexCount, int offset) {1490VKBuffer *ibuf = curIBuffer_;1491VKBuffer *vbuf = curVBuffer_;14921493VkBuffer vulkanVbuf, vulkanIbuf, vulkanUBObuf;1494uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);1495size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), 4, &vulkanVbuf);1496size_t ibBindOffset = push_->Push(ibuf->GetData(), ibuf->GetSize(), 4, &vulkanIbuf);14971498BindCurrentPipeline();1499ApplyDynamicState();1500int descSetIndex;1501PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex);1502BindDescriptors(vulkanUBObuf, descriptors);1503renderManager_.DrawIndexed(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1);1504}15051506void VKContext::DrawUP(const void *vdata, int vertexCount) {1507_dbg_assert_(vertexCount >= 0);1508if (vertexCount <= 0) {1509return;1510}15111512VkBuffer vulkanVbuf, vulkanUBObuf;1513size_t dataSize = vertexCount * curPipeline_->stride;1514uint32_t vbBindOffset;1515uint8_t *dataPtr = push_->Allocate(dataSize, 4, &vulkanVbuf, &vbBindOffset);1516_assert_(dataPtr != nullptr);1517memcpy(dataPtr, vdata, dataSize);15181519uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);15201521BindCurrentPipeline();1522ApplyDynamicState();1523int descSetIndex;1524PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex);1525BindDescriptors(vulkanUBObuf, descriptors);1526renderManager_.Draw(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vertexCount);1527}15281529void VKContext::BindCurrentPipeline() {1530renderManager_.BindPipeline(curPipeline_->pipeline, curPipeline_->flags, pipelineLayout_);1531}15321533void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) {1534int mask = 0;1535if (clearMask & FBChannel::FB_COLOR_BIT)1536mask |= VK_IMAGE_ASPECT_COLOR_BIT;1537if (clearMask & FBChannel::FB_DEPTH_BIT)1538mask |= VK_IMAGE_ASPECT_DEPTH_BIT;1539if (clearMask & FBChannel::FB_STENCIL_BIT)1540mask |= VK_IMAGE_ASPECT_STENCIL_BIT;1541renderManager_.Clear(colorval, depthVal, stencilVal, mask);1542}15431544DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan, bool useRenderThread) {1545return new VKContext(vulkan, useRenderThread);1546}15471548void AddFeature(std::vector<std::string> &features, const char *name, VkBool32 available, VkBool32 enabled) {1549char buf[512];1550snprintf(buf, sizeof(buf), "%s: Available: %d Enabled: %d", name, (int)available, (int)enabled);1551features.push_back(buf);1552}15531554std::vector<std::string> VKContext::GetFeatureList() const {1555const VkPhysicalDeviceFeatures &available = vulkan_->GetDeviceFeatures().available.standard;1556const VkPhysicalDeviceFeatures &enabled = vulkan_->GetDeviceFeatures().enabled.standard;15571558std::vector<std::string> features;1559AddFeature(features, "dualSrcBlend", available.dualSrcBlend, enabled.dualSrcBlend);1560AddFeature(features, "logicOp", available.logicOp, enabled.logicOp);1561AddFeature(features, "geometryShader", available.geometryShader, enabled.geometryShader);1562AddFeature(features, "depthBounds", available.depthBounds, enabled.depthBounds);1563AddFeature(features, "depthClamp", available.depthClamp, enabled.depthClamp);1564AddFeature(features, "pipelineStatisticsQuery", available.pipelineStatisticsQuery, enabled.pipelineStatisticsQuery);1565AddFeature(features, "samplerAnisotropy", available.samplerAnisotropy, enabled.samplerAnisotropy);1566AddFeature(features, "textureCompressionBC", available.textureCompressionBC, enabled.textureCompressionBC);1567AddFeature(features, "textureCompressionETC2", available.textureCompressionETC2, enabled.textureCompressionETC2);1568AddFeature(features, "textureCompressionASTC_LDR", available.textureCompressionASTC_LDR, enabled.textureCompressionASTC_LDR);1569AddFeature(features, "shaderClipDistance", available.shaderClipDistance, enabled.shaderClipDistance);1570AddFeature(features, "shaderCullDistance", available.shaderCullDistance, enabled.shaderCullDistance);1571AddFeature(features, "occlusionQueryPrecise", available.occlusionQueryPrecise, enabled.occlusionQueryPrecise);1572AddFeature(features, "multiDrawIndirect", available.multiDrawIndirect, enabled.multiDrawIndirect);1573AddFeature(features, "robustBufferAccess", available.robustBufferAccess, enabled.robustBufferAccess);1574AddFeature(features, "fullDrawIndexUint32", available.fullDrawIndexUint32, enabled.fullDrawIndexUint32);1575AddFeature(features, "fragmentStoresAndAtomics", available.fragmentStoresAndAtomics, enabled.fragmentStoresAndAtomics);1576AddFeature(features, "shaderInt16", available.shaderInt16, enabled.shaderInt16);15771578AddFeature(features, "multiview", vulkan_->GetDeviceFeatures().available.multiview.multiview, vulkan_->GetDeviceFeatures().enabled.multiview.multiview);1579AddFeature(features, "multiviewGeometryShader", vulkan_->GetDeviceFeatures().available.multiview.multiviewGeometryShader, vulkan_->GetDeviceFeatures().enabled.multiview.multiviewGeometryShader);1580AddFeature(features, "presentId", vulkan_->GetDeviceFeatures().available.presentId.presentId, vulkan_->GetDeviceFeatures().enabled.presentId.presentId);1581AddFeature(features, "presentWait", vulkan_->GetDeviceFeatures().available.presentWait.presentWait, vulkan_->GetDeviceFeatures().enabled.presentWait.presentWait);1582AddFeature(features, "provokingVertexLast", vulkan_->GetDeviceFeatures().available.provokingVertex.provokingVertexLast, vulkan_->GetDeviceFeatures().enabled.provokingVertex.provokingVertexLast);15831584features.emplace_back(std::string("Preferred depth buffer format: ") + VulkanFormatToString(vulkan_->GetDeviceInfo().preferredDepthStencilFormat));15851586return features;1587}15881589std::vector<std::string> VKContext::GetExtensionList(bool device, bool enabledOnly) const {1590std::vector<std::string> extensions;1591if (enabledOnly) {1592const auto& enabled = (device ? vulkan_->GetDeviceExtensionsEnabled() : vulkan_->GetInstanceExtensionsEnabled());1593extensions.reserve(enabled.size());1594for (auto &iter : enabled) {1595extensions.push_back(iter);1596}1597} else {1598const auto& available = (device ? vulkan_->GetDeviceExtensionsAvailable() : vulkan_->GetInstanceExtensionsAvailable());1599extensions.reserve(available.size());1600for (auto &iter : available) {1601extensions.push_back(iter.extensionName);1602}1603}1604return extensions;1605}16061607uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const {1608VkFormat vulkan_format = DataFormatToVulkan(fmt);1609VkFormatProperties properties;1610vkGetPhysicalDeviceFormatProperties(vulkan_->GetCurrentPhysicalDevice(), vulkan_format, &properties);1611uint32_t flags = 0;1612if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) {1613flags |= FMT_RENDERTARGET;1614}1615if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {1616flags |= FMT_DEPTHSTENCIL;1617}1618if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) {1619flags |= FMT_TEXTURE;1620}1621if (properties.bufferFeatures & VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT) {1622flags |= FMT_INPUTLAYOUT;1623}1624if ((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) {1625flags |= FMT_BLIT;1626}1627if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) {1628flags |= FMT_STORAGE_IMAGE;1629}1630return flags;1631}16321633// A VKFramebuffer is a VkFramebuffer (note caps difference) plus all the textures it owns.1634// It also has a reference to the command buffer that it was last rendered to with.1635// If it needs to be transitioned, and the frame number matches, use it, otherwise1636// use this frame's init command buffer.1637class VKFramebuffer : public Framebuffer {1638public:1639VKFramebuffer(VKRFramebuffer *fb, int multiSampleLevel) : buf_(fb) {1640_assert_msg_(fb, "Null fb in VKFramebuffer constructor");1641width_ = fb->width;1642height_ = fb->height;1643layers_ = fb->numLayers;1644multiSampleLevel_ = multiSampleLevel;1645}1646~VKFramebuffer() {1647_assert_msg_(buf_, "Null buf_ in VKFramebuffer - double delete?");1648buf_->Vulkan()->Delete().QueueCallback([](VulkanContext *vulkan, void *fb) {1649VKRFramebuffer *vfb = static_cast<VKRFramebuffer *>(fb);1650delete vfb;1651}, buf_);1652buf_ = nullptr;1653}1654VKRFramebuffer *GetFB() const { return buf_; }1655void UpdateTag(const char *newTag) override {1656buf_->UpdateTag(newTag);1657}1658private:1659VKRFramebuffer *buf_;1660};16611662Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {1663_assert_(desc.multiSampleLevel >= 0);1664_assert_(desc.numLayers > 0);1665_assert_(desc.width > 0);1666_assert_(desc.height > 0);16671668VkCommandBuffer cmd = renderManager_.GetInitCmd();1669VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, &renderManager_.PostInitBarrier(), cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.multiSampleLevel, desc.z_stencil, desc.tag);1670return new VKFramebuffer(vkrfb, desc.multiSampleLevel);1671}16721673void VKContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) {1674VKFramebuffer *src = (VKFramebuffer *)srcfb;1675VKFramebuffer *dst = (VKFramebuffer *)dstfb;16761677int aspectMask = 0;1678if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;1679if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;1680if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;16811682renderManager_.CopyFramebuffer(src->GetFB(), VkRect2D{ {x, y}, {(uint32_t)width, (uint32_t)height } }, dst->GetFB(), VkOffset2D{ dstX, dstY }, aspectMask, tag);1683}16841685bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) {1686VKFramebuffer *src = (VKFramebuffer *)srcfb;1687VKFramebuffer *dst = (VKFramebuffer *)dstfb;16881689int aspectMask = 0;1690if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;1691if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;1692if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;16931694renderManager_.BlitFramebuffer(src->GetFB(), VkRect2D{ {srcX1, srcY1}, {(uint32_t)(srcX2 - srcX1), (uint32_t)(srcY2 - srcY1) } }, dst->GetFB(), VkRect2D{ {dstX1, dstY1}, {(uint32_t)(dstX2 - dstX1), (uint32_t)(dstY2 - dstY1) } }, aspectMask, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST, tag);1695return true;1696}16971698bool VKContext::CopyFramebufferToMemory(Framebuffer *srcfb, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) {1699VKFramebuffer *src = (VKFramebuffer *)srcfb;17001701int aspectMask = 0;1702if (channelBits & FBChannel::FB_COLOR_BIT) aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;1703if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;1704if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;17051706return renderManager_.CopyFramebufferToMemory(src ? src->GetFB() : nullptr, aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride, mode, tag);1707}17081709DataFormat VKContext::PreferredFramebufferReadbackFormat(Framebuffer *src) {1710if (src) {1711return DrawContext::PreferredFramebufferReadbackFormat(src);1712}17131714if (vulkan_->GetSwapchainFormat() == VK_FORMAT_B8G8R8A8_UNORM) {1715return Draw::DataFormat::B8G8R8A8_UNORM;1716}1717return DrawContext::PreferredFramebufferReadbackFormat(src);1718}17191720void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {1721VKFramebuffer *fb = (VKFramebuffer *)fbo;1722VKRRenderPassLoadAction color = (VKRRenderPassLoadAction)rp.color;1723VKRRenderPassLoadAction depth = (VKRRenderPassLoadAction)rp.depth;1724VKRRenderPassLoadAction stencil = (VKRRenderPassLoadAction)rp.stencil;17251726renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil, tag);1727curFramebuffer_ = fb;1728}17291730void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {1731VKFramebuffer *fb = (VKFramebuffer *)fbo;1732_assert_(binding >= 0 && binding < MAX_BOUND_TEXTURES);17331734// TODO: There are cases where this is okay, actually. But requires layout transitions and stuff -1735// we're not ready for this.1736_assert_(fb != curFramebuffer_);17371738int aspect = 0;1739switch (channelBit) {1740case FBChannel::FB_COLOR_BIT:1741aspect = VK_IMAGE_ASPECT_COLOR_BIT;1742break;1743case FBChannel::FB_DEPTH_BIT:1744aspect = VK_IMAGE_ASPECT_DEPTH_BIT;1745break;1746default:1747_assert_(false);1748break;1749}17501751boundTextures_[binding].reset(nullptr);1752boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, layer);1753}17541755void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {1756VKFramebuffer *fb = (VKFramebuffer *)fbo;1757if (fb) {1758*w = fb->GetFB()->width;1759*h = fb->GetFB()->height;1760} else {1761*w = vulkan_->GetBackbufferWidth();1762*h = vulkan_->GetBackbufferHeight();1763}1764}17651766void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) {1767switch (ev) {1768case Event::LOST_BACKBUFFER:1769renderManager_.DestroyBackbuffers();1770break;1771case Event::GOT_BACKBUFFER:1772renderManager_.CreateBackbuffers();1773break;1774default:1775_assert_(false);1776break;1777}1778}17791780void VKContext::InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channels) {1781VkImageAspectFlags flags = 0;1782if (channels & FBChannel::FB_COLOR_BIT)1783flags |= VK_IMAGE_ASPECT_COLOR_BIT;1784if (channels & FBChannel::FB_DEPTH_BIT)1785flags |= VK_IMAGE_ASPECT_DEPTH_BIT;1786if (channels & FBChannel::FB_STENCIL_BIT)1787flags |= VK_IMAGE_ASPECT_STENCIL_BIT;1788if (stage == FB_INVALIDATION_LOAD) {1789renderManager_.SetLoadDontCare(flags);1790} else if (stage == FB_INVALIDATION_STORE) {1791renderManager_.SetStoreDontCare(flags);1792}1793}17941795uint64_t VKContext::GetNativeObject(NativeObject obj, void *srcObject) {1796switch (obj) {1797case NativeObject::CONTEXT:1798return (uint64_t)vulkan_;1799case NativeObject::INIT_COMMANDBUFFER:1800return (uint64_t)renderManager_.GetInitCmd();1801case NativeObject::BOUND_TEXTURE0_IMAGEVIEW:1802return (uint64_t)boundImageView_[0];1803case NativeObject::BOUND_TEXTURE1_IMAGEVIEW:1804return (uint64_t)boundImageView_[1];1805case NativeObject::RENDER_MANAGER:1806return (uint64_t)(uintptr_t)&renderManager_;1807case NativeObject::NULL_IMAGEVIEW:1808return (uint64_t)GetNullTexture()->GetImageView();1809case NativeObject::NULL_IMAGEVIEW_ARRAY:1810return (uint64_t)GetNullTexture()->GetImageArrayView();1811case NativeObject::TEXTURE_VIEW:1812return (uint64_t)(((VKTexture *)srcObject)->GetImageView());1813case NativeObject::BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW_ALL_LAYERS:1814return (uint64_t)curFramebuffer_->GetFB()->color.texAllLayersView;1815case NativeObject::BOUND_FRAMEBUFFER_COLOR_IMAGEVIEW_RT:1816return (uint64_t)curFramebuffer_->GetFB()->GetRTView();1817case NativeObject::THIN3D_PIPELINE_LAYOUT:1818return (uint64_t)pipelineLayout_;1819case NativeObject::PUSH_POOL:1820return (uint64_t)push_;1821default:1822Crash();1823return 0;1824}1825}18261827void VKContext::DebugAnnotate(const char *annotation) {1828renderManager_.DebugAnnotate(annotation);1829}18301831} // namespace Draw183218331834