ppsspp/ext/native/thin3d/thin3d_vulkan.cpp

2037 lines
74 KiB
C++
Raw Normal View History

// Copyright (c) 2015- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2016-12-25 18:18:19 +01:00
#include <cstdio>
#include <vector>
#include <string>
#include <map>
#include <assert.h>
#include "Common/Vulkan/SPIRVDisasm.h"
#include "base/logging.h"
#include "base/display.h"
#include "base/stringutil.h"
#include "image/zim_load.h"
#include "math/lin/matrix4x4.h"
#include "math/dataconv.h"
#include "thin3d/thin3d.h"
#include "Common/Vulkan/VulkanContext.h"
#include "Common/Vulkan/VulkanImage.h"
#include "Common/Vulkan/VulkanMemory.h"
// We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point.
2016-01-02 02:08:05 +01:00
// binding 0 - uniform data
// binding 1 - sampler
//
// Vertex data lives in a separate namespace (location = 0, 1, etc)
#include "Common/Vulkan/VulkanLoader.h"
2016-12-25 18:18:19 +01:00
namespace Draw {
// This can actually be replaced with a cast as the values are in the right order.
static const VkCompareOp compToVK[] = {
VK_COMPARE_OP_NEVER,
VK_COMPARE_OP_LESS,
VK_COMPARE_OP_EQUAL,
VK_COMPARE_OP_LESS_OR_EQUAL,
VK_COMPARE_OP_GREATER,
VK_COMPARE_OP_NOT_EQUAL,
VK_COMPARE_OP_GREATER_OR_EQUAL,
VK_COMPARE_OP_ALWAYS
};
// So can this.
2016-12-26 18:32:52 +01:00
static const VkBlendOp blendEqToVk[] = {
VK_BLEND_OP_ADD,
VK_BLEND_OP_SUBTRACT,
VK_BLEND_OP_REVERSE_SUBTRACT,
VK_BLEND_OP_MIN,
VK_BLEND_OP_MAX,
};
static const VkBlendFactor blendFactorToVk[] = {
VK_BLEND_FACTOR_ZERO,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_SRC_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_DST_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_DST_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
VK_BLEND_FACTOR_CONSTANT_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
VK_BLEND_FACTOR_CONSTANT_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
VK_BLEND_FACTOR_SRC1_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR,
VK_BLEND_FACTOR_SRC1_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA,
};
static const VkLogicOp logicOpToVK[] = {
VK_LOGIC_OP_CLEAR,
VK_LOGIC_OP_SET,
VK_LOGIC_OP_COPY,
VK_LOGIC_OP_COPY_INVERTED,
VK_LOGIC_OP_NO_OP,
VK_LOGIC_OP_INVERT,
VK_LOGIC_OP_AND,
VK_LOGIC_OP_NAND,
VK_LOGIC_OP_OR,
VK_LOGIC_OP_NOR,
VK_LOGIC_OP_XOR,
VK_LOGIC_OP_EQUIVALENT,
VK_LOGIC_OP_AND_REVERSE,
VK_LOGIC_OP_AND_INVERTED,
VK_LOGIC_OP_OR_REVERSE,
VK_LOGIC_OP_OR_INVERTED,
};
static const VkPrimitiveTopology primToVK[] = {
VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
// Tesselation shader primitive.
VK_PRIMITIVE_TOPOLOGY_PATCH_LIST,
// The rest are for geometry shaders only.
VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
};
static const VkStencilOp stencilOpToVK[8] = {
VK_STENCIL_OP_KEEP,
VK_STENCIL_OP_ZERO,
VK_STENCIL_OP_REPLACE,
VK_STENCIL_OP_INCREMENT_AND_CLAMP,
VK_STENCIL_OP_DECREMENT_AND_CLAMP,
VK_STENCIL_OP_INVERT,
VK_STENCIL_OP_INCREMENT_AND_WRAP,
VK_STENCIL_OP_DECREMENT_AND_WRAP,
};
static inline void Uint8x4ToFloat4(uint32_t u, float f[4]) {
f[0] = ((u >> 0) & 0xFF) * (1.0f / 255.0f);
f[1] = ((u >> 8) & 0xFF) * (1.0f / 255.0f);
f[2] = ((u >> 16) & 0xFF) * (1.0f / 255.0f);
f[3] = ((u >> 24) & 0xFF) * (1.0f / 255.0f);
}
2016-12-25 21:21:56 +01:00
class VKBlendState : public BlendState {
public:
VkPipelineColorBlendStateCreateInfo info{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
2016-12-26 18:32:52 +01:00
std::vector<VkPipelineColorBlendAttachmentState> attachments;
};
2016-12-25 21:21:56 +01:00
class VKDepthStencilState : public DepthStencilState {
public:
VkPipelineDepthStencilStateCreateInfo info{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
};
2016-12-25 21:21:56 +01:00
class VKRasterState : public RasterState {
public:
2016-12-25 22:24:14 +01:00
VKRasterState(VulkanContext *vulkan, const RasterStateDesc &desc) {
cullFace = desc.cull;
2016-12-27 15:52:03 +01:00
frontFace = desc.frontFace;
}
Facing frontFace;
CullMode cullFace;
void ToVulkan(VkPipelineRasterizationStateCreateInfo *info) const {
memset(info, 0, sizeof(*info));
info->sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
info->frontFace = frontFace == Facing::CCW ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE;
switch (cullFace) {
case CullMode::BACK: info->cullMode = VK_CULL_MODE_BACK_BIT; break;
case CullMode::FRONT: info->cullMode = VK_CULL_MODE_FRONT_BIT; break;
case CullMode::FRONT_AND_BACK: info->cullMode = VK_CULL_MODE_FRONT_AND_BACK; break;
case CullMode::NONE: info->cullMode = VK_CULL_MODE_NONE; break;
}
info->polygonMode = VK_POLYGON_MODE_FILL;
info->lineWidth = 1.0f;
}
};
2016-12-25 22:24:14 +01:00
VkShaderStageFlagBits StageToVulkan(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX: return VK_SHADER_STAGE_VERTEX_BIT;
case ShaderStage::GEOMETRY: return VK_SHADER_STAGE_GEOMETRY_BIT;
case ShaderStage::COMPUTE: return VK_SHADER_STAGE_COMPUTE_BIT;
case ShaderStage::EVALUATION: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
case ShaderStage::CONTROL: return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
default:
case ShaderStage::FRAGMENT: return VK_SHADER_STAGE_FRAGMENT_BIT;
}
}
2016-12-26 13:42:53 +01:00
// Not registering this as a resource holder, instead the pipeline is registered. It will
// invoke Compile again to recreate the shader then link them together.
2016-12-25 22:24:14 +01:00
class VKShaderModule : public ShaderModule {
public:
2016-12-25 22:24:14 +01:00
VKShaderModule(ShaderStage stage) : module_(VK_NULL_HANDLE), ok_(false), stage_(stage) {
vkstage_ = StageToVulkan(stage);
}
bool Compile(VulkanContext *vulkan, ShaderLanguage language, const uint8_t *data, size_t size);
const std::string &GetSource() const { return source_; }
2016-12-25 22:24:14 +01:00
~VKShaderModule() {
if (module_) {
vkDestroyShaderModule(device_, module_, nullptr);
}
}
VkShaderModule Get() const { return module_; }
2016-12-25 22:24:14 +01:00
ShaderStage GetStage() const override {
return stage_;
}
private:
VkDevice device_;
VkShaderModule module_;
2016-12-25 22:24:14 +01:00
VkShaderStageFlagBits vkstage_;
bool ok_;
2016-12-25 22:24:14 +01:00
ShaderStage stage_;
std::string source_; // So we can recompile in case of context loss.
};
bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, const uint8_t *data, size_t size) {
// We'll need this to free it later.
device_ = vulkan->GetDevice();
this->source_ = (const char *)data;
std::vector<uint32_t> spirv;
if (!GLSLtoSPV(vkstage_, source_.c_str(), spirv)) {
return false;
}
// Just for kicks, sanity check the SPIR-V. The disasm isn't perfect
// but gives you some idea of what's going on.
#if 0
std::string disasm;
if (DisassembleSPIRV(spirv, &disasm)) {
OutputDebugStringA(disasm.c_str());
}
#endif
if (vulkan->CreateShaderModule(spirv, &module_)) {
ok_ = true;
} else {
ok_ = false;
}
return ok_;
}
inline VkFormat ConvertVertexDataTypeToVk(DataFormat type) {
switch (type) {
2016-12-25 21:21:56 +01:00
case DataFormat::R32G32_FLOAT: return VK_FORMAT_R32G32_SFLOAT;
case DataFormat::R32G32B32_FLOAT: return VK_FORMAT_R32G32B32_SFLOAT;
case DataFormat::R32G32B32A32_FLOAT: return VK_FORMAT_R32G32B32A32_SFLOAT;
case DataFormat::R8G8B8A8_UNORM: return VK_FORMAT_R8G8B8A8_UNORM;
default: return VK_FORMAT_UNDEFINED;
}
}
class VKInputLayout : public InputLayout {
public:
std::vector<VkVertexInputBindingDescription> bindings;
std::vector<VkVertexInputAttributeDescription> attributes;
VkPipelineVertexInputStateCreateInfo visc;
};
class VKPipeline : public Pipeline {
public:
2017-05-07 10:53:04 +02:00
VKPipeline(VulkanContext *vulkan, size_t size) : vulkan_(vulkan) {
uboSize_ = (int)size;
ubo_ = new uint8_t[uboSize_];
}
~VKPipeline() {
2017-05-07 10:53:04 +02:00
vulkan_->Delete().QueueDeletePipeline(vkpipeline);
delete[] ubo_;
}
void SetDynamicUniformData(const void *data, size_t size) {
memcpy(ubo_, data, size);
}
// Returns the binding offset, and the VkBuffer to bind.
size_t PushUBO(VulkanPushBuffer *buf, VulkanContext *vulkan, VkBuffer *vkbuf) {
return buf->PushAligned(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment, vkbuf);
}
int GetUniformLoc(const char *name);
2016-01-02 02:08:05 +01:00
int GetUBOSize() const {
return uboSize_;
}
2017-01-17 19:41:53 +07:00
bool RequiresBuffer() override {
return false;
}
2016-01-02 02:08:05 +01:00
VkPipeline vkpipeline;
int stride[4]{};
int dynamicUniformSize = 0;
private:
2017-05-07 10:53:04 +02:00
VulkanContext *vulkan_;
uint8_t *ubo_;
int uboSize_;
};
2016-12-25 21:21:56 +01:00
class VKTexture;
class VKBuffer;
2016-12-25 21:21:56 +01:00
class VKSamplerState;
struct DescriptorSetKey {
2016-12-25 21:21:56 +01:00
VKTexture *texture_;
VKSamplerState *sampler_;
VkBuffer buffer_;
bool operator < (const DescriptorSetKey &other) const {
if (texture_ < other.texture_) return true; else if (texture_ > other.texture_) return false;
if (sampler_ < other.sampler_) return true; else if (sampler_ > other.sampler_) return false;
if (buffer_ < other.buffer_) return true; else if (buffer_ > other.buffer_) return false;
return false;
}
};
class VKTexture : public Texture {
public:
VKTexture(VulkanContext *vulkan, const TextureDesc &desc)
: vulkan_(vulkan), format_(desc.format), mipLevels_(desc.mipLevels) {
Create(desc);
}
~VKTexture() {
Destroy();
}
VkImageView GetImageView() { return vkTex_->GetImageView(); }
private:
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data);
bool Create(const TextureDesc &desc);
void Destroy() {
if (vkTex_) {
vkTex_->Destroy();
delete vkTex_;
}
}
VulkanContext *vulkan_;
VulkanTexture *vkTex_;
int mipLevels_;
DataFormat format_;
};
// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine
// to let them have individual non-pooled allocations. Until it's not fine. We'll see.
struct VKImage {
VkImage image;
VkImageView view;
VkDeviceMemory memory;
VkImageLayout layout;
};
2016-12-25 21:21:56 +01:00
class VKContext : public DrawContext {
public:
2016-12-25 21:21:56 +01:00
VKContext(VulkanContext *vulkan);
virtual ~VKContext();
const DeviceCaps &GetDeviceCaps() const override {
return caps_;
}
uint32_t GetSupportedShaderLanguages() const override {
return (uint32_t)ShaderLanguage::GLSL_VULKAN | (uint32_t)ShaderLanguage::SPIRV_VULKAN;
}
uint32_t GetDataFormatSupport(DataFormat fmt) const override;
2016-12-25 21:10:46 +01:00
DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
2016-12-25 20:54:37 +01:00
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
2016-12-25 22:24:14 +01:00
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
2016-12-26 13:42:53 +01:00
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize) override;
2016-12-25 22:24:14 +01:00
Texture *CreateTexture(const TextureDesc &desc) override;
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
2017-02-07 18:16:52 +01:00
void 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) override;
bool 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) override;
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
// color must be 0, for now.
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
void BindFramebufferForRead(Framebuffer *fbo) override;
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
void SetScissorRect(int left, int top, int width, int height) override;
void SetViewports(int count, Viewport *viewports) override;
2016-12-27 15:52:03 +01:00
void SetBlendFactor(float color[4]) override;
2016-12-26 11:16:59 +01:00
void BindSamplerStates(int start, int count, SamplerState **state) override;
2016-12-25 20:54:37 +01:00
void BindTextures(int start, int count, Texture **textures) override;
2016-12-26 11:16:59 +01:00
void BindPipeline(Pipeline *pipeline) override {
curPipeline_ = (VKPipeline *)pipeline;
}
// TODO: Make VKBuffers proper buffers, and do a proper binding model. This is just silly.
void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) override {
for (int i = 0; i < count; i++) {
curVBuffers_[i + start] = (VKBuffer *)buffers[i];
curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0;
}
}
void BindIndexBuffer(Buffer *indexBuffer, int offset) override {
curIBuffer_ = (VKBuffer *)indexBuffer;
curIBufferOffset_ = offset;
}
void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;
// TODO: Add more sophisticated draws.
void Draw(int vertexCount, int offset) override;
void DrawIndexed(int vertexCount, int offset) override;
void DrawUP(const void *vdata, int vertexCount) override;
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;
void BeginFrame() override;
void EndFrame() override;
void FlushState() override {
ApplyDynamicState();
}
void WaitRenderCompletion(Framebuffer *fbo) override;
std::string GetInfoString(InfoField info) const override {
// TODO: Make these actually query the right information
switch (info) {
case APINAME: return "Vulkan";
case VENDORSTRING: return vulkan_->GetPhysicalDeviceProperties().deviceName;
case VENDOR: return StringFromFormat("%08x", vulkan_->GetPhysicalDeviceProperties().vendorID);
case DRIVER: return StringFromFormat("%08x", vulkan_->GetPhysicalDeviceProperties().driverVersion);
case SHADELANGVERSION: return "N/A";;
case APIVERSION:
{
uint32_t ver = vulkan_->GetPhysicalDeviceProperties().apiVersion;
return StringFromFormat("%d.%d.%d", ver >> 22, (ver >> 12) & 0x3ff, ver & 0xfff);
}
default: return "?";
}
}
VkDescriptorSet GetOrCreateDescriptorSet(VkBuffer buffer);
std::vector<std::string> GetFeatureList() const override;
2017-02-05 20:05:03 +01:00
uintptr_t GetNativeObject(NativeObject obj) const override {
switch (obj) {
case NativeObject::COMPATIBLE_RENDERPASS:
// Return a representative renderpass.
if (curRenderPass_ == vulkan_->GetSurfaceRenderPass())
return (uintptr_t)curRenderPass_;
else
return (uintptr_t)renderPasses_[0];
case NativeObject::CURRENT_RENDERPASS:
return (uintptr_t)curRenderPass_;
case NativeObject::RENDERPASS_COMMANDBUFFER:
return (uintptr_t)cmd_;
case NativeObject::BOUND_TEXTURE_IMAGEVIEW:
return (uintptr_t)boundImageView_[0];
default:
return 0;
}
2017-02-05 20:05:03 +01:00
}
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
private:
void ApplyDynamicState();
void DirtyDynamicState();
void EndCurrentRenderpass();
VkCommandBuffer AllocCmdBuf();
static void SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect);
static void SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect);
VulkanContext *vulkan_ = nullptr;
VKPipeline *curPipeline_ = nullptr;
VKBuffer *curVBuffers_[4]{};
int curVBufferOffsets_[4]{};
VKBuffer *curIBuffer_ = nullptr;
int curIBufferOffset_ = 0;
VkDescriptorSetLayout descriptorSetLayout_;
VkPipelineLayout pipelineLayout_;
VkPipelineCache pipelineCache_;
inline int RPIndex(RPAction color, RPAction depth) {
return (int)depth * 3 + (int)color;
}
// Renderpasses, all combination of preserving or clearing or dont-care-ing fb contents.
VkRenderPass renderPasses_[9];
VkDevice device_;
VkQueue queue_;
int queueFamilyIndex_;
// State to apply at the next draw call if viewportDirty or scissorDirty are true.
bool viewportDirty_;
VkViewport viewport_{};
bool scissorDirty_;
VkRect2D scissor_;
int curWidth_ = -1;
int curHeight_ = -1;
enum {
MAX_BOUND_TEXTURES = 1,
2017-05-22 15:29:14 +02:00
MAX_FRAME_COMMAND_BUFFERS = 256,
};
2016-12-25 21:21:56 +01:00
VKTexture *boundTextures_[MAX_BOUND_TEXTURES];
VKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES];
VkImageView boundImageView_[MAX_BOUND_TEXTURES];
2016-01-03 12:37:05 +01:00
struct FrameData {
VulkanPushBuffer *pushBuffer;
VkCommandPool cmdPool_;
VkCommandBuffer cmdBufs[MAX_FRAME_COMMAND_BUFFERS];
int startCmdBufs_;
int numCmdBufs;
// Per-frame descriptor set cache. As it's per frame and reset every frame, we don't need to
// worry about invalidating descriptors pointing to deleted textures.
// However! ARM is not a fan of doing it this way.
std::map<DescriptorSetKey, VkDescriptorSet> descSets_;
VkDescriptorPool descriptorPool;
2016-01-03 12:37:05 +01:00
};
FrameData frame_[2]{};
2016-01-03 12:37:05 +01:00
int frameNum_ = 0;
VulkanPushBuffer *push_ = nullptr;
DeviceCaps caps_{};
2017-05-23 21:56:48 +02:00
VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE;
VkRenderPass curRenderPass_ = VK_NULL_HANDLE;
VkCommandBuffer cmd_ = VK_NULL_HANDLE;
};
static int GetBpp(VkFormat format) {
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_B8G8R8A8_UNORM:
return 32;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
case VK_FORMAT_R5G6B5_UNORM_PACK16:
case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
case VK_FORMAT_B5G6R5_UNORM_PACK16:
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
return 16;
case VK_FORMAT_D24_UNORM_S8_UINT:
return 32;
case VK_FORMAT_D16_UNORM:
return 16;
default:
return 0;
}
}
VkFormat DataFormatToVulkan(DataFormat format) {
switch (format) {
case DataFormat::D16: return VK_FORMAT_D16_UNORM;
case DataFormat::D32F: return VK_FORMAT_D32_SFLOAT;
case DataFormat::D32F_S8: return VK_FORMAT_D32_SFLOAT_S8_UINT;
case DataFormat::S8: return VK_FORMAT_S8_UINT;
case DataFormat::R16_FLOAT: return VK_FORMAT_R16_SFLOAT;
case DataFormat::R16G16_FLOAT: return VK_FORMAT_R16G16_SFLOAT;
case DataFormat::R16G16B16A16_FLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT;
case DataFormat::R8_UNORM: return VK_FORMAT_R8_UNORM;
case DataFormat::R8G8_UNORM: return VK_FORMAT_R8G8_UNORM;
case DataFormat::R8G8B8_UNORM: return VK_FORMAT_R8G8B8_UNORM;
case DataFormat::R8G8B8A8_UNORM: return VK_FORMAT_R8G8B8A8_UNORM;
case DataFormat::R4G4_UNORM_PACK8: return VK_FORMAT_R4G4_UNORM_PACK8;
case DataFormat::R4G4B4A4_UNORM_PACK16: return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
case DataFormat::B4G4R4A4_UNORM_PACK16: return VK_FORMAT_B4G4R4A4_UNORM_PACK16;
case DataFormat::R5G5B5A1_UNORM_PACK16: return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
case DataFormat::B5G5R5A1_UNORM_PACK16: return VK_FORMAT_B5G5R5A1_UNORM_PACK16;
case DataFormat::R5G6B5_UNORM_PACK16: return VK_FORMAT_R5G6B5_UNORM_PACK16;
case DataFormat::B5G6R5_UNORM_PACK16: return VK_FORMAT_B5G6R5_UNORM_PACK16;
case DataFormat::A1R5G5B5_UNORM_PACK16: return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
case DataFormat::R32_FLOAT: return VK_FORMAT_R32_SFLOAT;
case DataFormat::R32G32_FLOAT: return VK_FORMAT_R32G32_SFLOAT;
case DataFormat::R32G32B32_FLOAT: return VK_FORMAT_R32G32B32_SFLOAT;
case DataFormat::R32G32B32A32_FLOAT: return VK_FORMAT_R32G32B32A32_SFLOAT;
case DataFormat::BC1_RGBA_UNORM_BLOCK: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
case DataFormat::BC2_UNORM_BLOCK: return VK_FORMAT_BC2_UNORM_BLOCK;
case DataFormat::BC3_UNORM_BLOCK: return VK_FORMAT_BC3_UNORM_BLOCK;
case DataFormat::BC4_UNORM_BLOCK: return VK_FORMAT_BC4_UNORM_BLOCK;
case DataFormat::BC4_SNORM_BLOCK: return VK_FORMAT_BC4_SNORM_BLOCK;
case DataFormat::BC5_UNORM_BLOCK: return VK_FORMAT_BC5_UNORM_BLOCK;
case DataFormat::BC5_SNORM_BLOCK: return VK_FORMAT_BC5_SNORM_BLOCK;
case DataFormat::BC6H_SFLOAT_BLOCK: return VK_FORMAT_BC6H_SFLOAT_BLOCK;
case DataFormat::BC6H_UFLOAT_BLOCK: return VK_FORMAT_BC6H_UFLOAT_BLOCK;
case DataFormat::BC7_UNORM_BLOCK: return VK_FORMAT_BC7_UNORM_BLOCK;
case DataFormat::BC7_SRGB_BLOCK: return VK_FORMAT_BC7_SRGB_BLOCK;
default:
return VK_FORMAT_UNDEFINED;
}
}
2016-12-25 21:21:56 +01:00
inline VkSamplerAddressMode AddressModeToVulkan(Draw::TextureAddressMode mode) {
switch (mode) {
case TextureAddressMode::CLAMP_TO_BORDER: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case TextureAddressMode::CLAMP_TO_EDGE: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case TextureAddressMode::REPEAT_MIRROR: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
default:
case TextureAddressMode::REPEAT: return VK_SAMPLER_ADDRESS_MODE_REPEAT;
}
}
class VKSamplerState : public SamplerState {
public:
2016-12-25 21:21:56 +01:00
VKSamplerState(VulkanContext *vulkan, const SamplerStateDesc &desc) : vulkan_(vulkan) {
VkSamplerCreateInfo s = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
2016-12-25 21:21:56 +01:00
s.addressModeU = AddressModeToVulkan(desc.wrapU);
s.addressModeV = AddressModeToVulkan(desc.wrapV);
s.addressModeW = AddressModeToVulkan(desc.wrapW);
s.anisotropyEnable = desc.maxAniso > 1.0f;
s.magFilter = desc.magFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
s.minFilter = desc.minFilter == TextureFilter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
s.mipmapMode = desc.mipFilter == TextureFilter::LINEAR ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST;
s.maxLod = desc.maxLod;
2016-01-03 12:37:05 +01:00
VkResult res = vkCreateSampler(vulkan_->GetDevice(), &s, nullptr, &sampler_);
2015-12-20 23:39:03 +01:00
assert(VK_SUCCESS == res);
}
2016-12-25 21:21:56 +01:00
~VKSamplerState() {
2017-05-07 10:40:11 +02:00
vulkan_->Delete().QueueDeleteSampler(sampler_);
}
VkSampler GetSampler() { return sampler_; }
private:
VulkanContext *vulkan_;
VkSampler sampler_;
};
2016-12-25 21:21:56 +01:00
SamplerState *VKContext::CreateSamplerState(const SamplerStateDesc &desc) {
return new VKSamplerState(vulkan_, desc);
}
2016-12-25 22:24:14 +01:00
RasterState *VKContext::CreateRasterState(const RasterStateDesc &desc) {
2016-12-25 21:21:56 +01:00
return new VKRasterState(vulkan_, desc);
}
2016-12-26 11:16:59 +01:00
void VKContext::BindSamplerStates(int start, int count, SamplerState **state) {
for (int i = start; i < start + count; i++) {
2016-12-25 21:21:56 +01:00
boundSamplers_[i] = (VKSamplerState *)state[i];
}
}
enum class TextureState {
UNINITIALIZED,
STAGED,
INITIALIZED,
PENDING_DESTRUCTION,
};
bool VKTexture::Create(const TextureDesc &desc) {
// Zero-sized textures not allowed.
if (desc.width * desc.height * desc.depth == 0)
return false;
format_ = desc.format;
mipLevels_ = desc.mipLevels;
width_ = desc.width;
height_ = desc.height;
depth_ = desc.depth;
vkTex_ = new VulkanTexture(vulkan_);
if (desc.initData.size()) {
for (int i = 0; i < (int)desc.initData.size(); i++) {
this->SetImageData(0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]);
2017-01-18 00:31:44 +07:00
}
}
return true;
}
2016-12-25 21:21:56 +01:00
VKContext::VKContext(VulkanContext *vulkan)
: viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0), caps_{} {
caps_.anisoSupported = vulkan->GetFeaturesAvailable().samplerAnisotropy != 0;
caps_.geometryShaderSupported = vulkan->GetFeaturesAvailable().geometryShader != 0;
caps_.tesselationShaderSupported = vulkan->GetFeaturesAvailable().tessellationShader != 0;
caps_.multiViewport = vulkan->GetFeaturesAvailable().multiViewport != 0;
caps_.dualSourceBlend = vulkan->GetFeaturesAvailable().dualSrcBlend != 0;
caps_.framebufferBlitSupported = true;
caps_.framebufferCopySupported = true;
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
2015-12-31 01:07:06 +01:00
device_ = vulkan->GetDevice();
2016-01-02 02:08:05 +01:00
queue_ = vulkan->GetGraphicsQueue();
queueFamilyIndex_ = vulkan->GetGraphicsQueueFamilyIndex();
scissor_.offset.x = 0;
scissor_.offset.y = 0;
scissor_.extent.width = pixel_xres;
scissor_.extent.height = pixel_yres;
2016-01-02 02:08:05 +01:00
viewport_.x = 0;
viewport_.y = 0;
viewport_.width = pixel_xres;
viewport_.height = pixel_yres;
viewport_.minDepth = 0.0f;
viewport_.maxDepth = 0.0f;
memset(boundTextures_, 0, sizeof(boundTextures_));
CreatePresets();
2016-01-02 02:08:05 +01:00
VkDescriptorPoolSize dpTypes[2];
dpTypes[0].descriptorCount = 200;
2016-01-03 12:37:05 +01:00
dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
dpTypes[1].descriptorCount = 200;
dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
dp.flags = 0; // Don't want to mess around with individually freeing these, let's go dynamic each frame.
dp.maxSets = 200; // 200 textures per frame should be enough for the UI...
dp.pPoolSizes = dpTypes;
dp.poolSizeCount = ARRAY_SIZE(dpTypes);
2016-01-03 12:37:05 +01:00
VkCommandPoolCreateInfo p = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
p.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
p.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();
for (int i = 0; i < 2; i++) {
VkResult res = vkCreateDescriptorPool(device_, &dp, nullptr, &frame_[i].descriptorPool);
assert(VK_SUCCESS == res);
res = vkCreateCommandPool(device_, &p, nullptr, &frame_[i].cmdPool_);
assert(VK_SUCCESS == res);
frame_[i].pushBuffer = new VulkanPushBuffer(vulkan_, 1024 * 1024);
}
2016-01-02 02:08:05 +01:00
// binding 0 - uniform data
2016-01-03 18:31:03 +01:00
// binding 1 - combined sampler/image
2016-01-02 02:08:05 +01:00
VkDescriptorSetLayoutBinding bindings[2];
bindings[0].descriptorCount = 1;
bindings[0].pImmutableSamplers = nullptr;
2016-01-02 02:08:05 +01:00
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
2015-12-31 01:33:23 +01:00
bindings[0].binding = 0;
bindings[1].descriptorCount = 1;
bindings[1].pImmutableSamplers = nullptr;
2016-01-02 02:08:05 +01:00
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
2015-12-31 01:33:23 +01:00
bindings[1].binding = 1;
VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
2016-01-02 02:08:05 +01:00
dsl.bindingCount = 2;
dsl.pBindings = bindings;
VkResult res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &descriptorSetLayout_);
assert(VK_SUCCESS == res);
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pl.pPushConstantRanges = nullptr;
pl.pushConstantRangeCount = 0;
pl.setLayoutCount = 1;
pl.pSetLayouts = &descriptorSetLayout_;
res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_);
assert(VK_SUCCESS == res);
2016-01-03 18:31:03 +01:00
pipelineCache_ = vulkan_->CreatePipelineCache();
// Create a bunch of render pass objects, for normal rendering with a depth buffer,
2017-05-23 21:56:48 +02:00
// with clearing, without clearing, and dont-care for both depth/stencil and color, so 3*3=9 combos.
VkAttachmentDescription attachments[2] = {};
attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM;
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[0].flags = 0;
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].stencilLoadOp = 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;
attachments[1].flags = 0;
VkAttachmentReference color_reference = {};
color_reference.attachment = 0;
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_reference = {};
depth_reference.attachment = 1;
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.flags = 0;
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = NULL;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.pResolveAttachments = NULL;
subpass.pDepthStencilAttachment = &depth_reference;
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = NULL;
VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = 2;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;
rp.dependencyCount = 0;
rp.pDependencies = NULL;
for (int depth = 0; depth < 3; depth++) {
switch ((RPAction)depth) {
case RPAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break;
case RPAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break;
case RPAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break;
}
for (int color = 0; color < 3; color++) {
switch ((RPAction)color) {
2017-05-22 15:29:14 +02:00
case RPAction::CLEAR: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break;
case RPAction::KEEP: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break;
case RPAction::DONT_CARE: attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break;
}
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RPAction)color, (RPAction)depth)]);
}
}
}
2016-12-25 21:21:56 +01:00
VKContext::~VKContext() {
for (int i = 0; i < 9; i++) {
vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]);
}
// This also destroys all descriptor sets.
for (int i = 0; i < 2; i++) {
frame_[i].descSets_.clear();
2017-05-07 10:40:11 +02:00
vulkan_->Delete().QueueDeleteDescriptorPool(frame_[i].descriptorPool);
frame_[i].pushBuffer->Destroy(vulkan_);
delete frame_[i].pushBuffer;
vulkan_->Delete().QueueDeleteCommandPool(frame_[i].cmdPool_);
}
2017-05-07 10:40:11 +02:00
vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_);
vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_);
vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
}
// Effectively wiped every frame, just allocate new ones!
VkCommandBuffer VKContext::AllocCmdBuf() {
FrameData *frame = &frame_[frameNum_ & 1];
2017-05-23 21:56:48 +02:00
if (frame->numCmdBufs >= MAX_FRAME_COMMAND_BUFFERS)
Crash();
if (frame->cmdBufs[frame->numCmdBufs]) {
VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++];
if (!cmdBuf)
Crash();
return cmdBuf;
}
VkCommandBufferAllocateInfo alloc{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
alloc.commandBufferCount = 1;
alloc.commandPool = frame->cmdPool_;
alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
VkResult result = vkAllocateCommandBuffers(vulkan_->GetDevice(), &alloc, &frame->cmdBufs[frame->numCmdBufs]);
assert(result == VK_SUCCESS);
VkCommandBuffer cmdBuf = frame->cmdBufs[frame->numCmdBufs++];
if (!cmdBuf)
Crash();
return cmdBuf;
}
void VKContext::BeginFrame() {
vulkan_->BeginFrame();
FrameData &frame = frame_[frameNum_ & 1];
frame.startCmdBufs_ = 0;
frame.numCmdBufs = 0;
vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0);
push_ = frame.pushBuffer;
2016-01-02 02:08:05 +01:00
// OK, we now know that nothing is reading from this frame's data pushbuffer,
push_->Reset();
push_->Begin(vulkan_);
frame.descSets_.clear();
VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0);
assert(result == VK_SUCCESS);
scissor_.extent.width = pixel_xres;
scissor_.extent.height = pixel_yres;
2016-01-02 02:08:05 +01:00
scissorDirty_ = true;
viewportDirty_ = true;
}
void VKContext::WaitRenderCompletion(Framebuffer *fbo) {
// TODO
}
void VKContext::EndFrame() {
if (curRenderPass_) {
2017-05-22 15:29:14 +02:00
// ELOG("EndFrame: Ending render pass");
vulkan_->EndSurfaceRenderPass();
curRenderPass_ = VK_NULL_HANDLE;
curFramebuffer_ = VK_NULL_HANDLE;
cmd_ = nullptr;
}
if (cmd_)
Crash();
// Cap off and submit all the command buffers we recorded during the frame.
FrameData &frame = frame_[frameNum_ & 1];
for (int i = frame.startCmdBufs_; i < frame.numCmdBufs; i++) {
vkEndCommandBuffer(frame.cmdBufs[i]);
vulkan_->QueueBeforeSurfaceRender(frame.cmdBufs[i]);
}
frame.startCmdBufs_ = frame.numCmdBufs;
2016-01-03 12:37:05 +01:00
// Stop collecting data in the frame's data pushbuffer.
push_->End();
vulkan_->EndFrame();
frameNum_++;
2016-01-02 02:08:05 +01:00
push_ = nullptr;
DirtyDynamicState();
}
2016-12-25 21:21:56 +01:00
VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
DescriptorSetKey key;
FrameData *frame = &frame_[frameNum_ & 1];
key.texture_ = boundTextures_[0];
key.sampler_ = boundSamplers_[0];
key.buffer_ = buf;
auto iter = frame->descSets_.find(key);
if (iter != frame->descSets_.end()) {
return iter->second;
}
VkDescriptorSet descSet;
2016-03-21 00:35:27 -07:00
VkDescriptorSetAllocateInfo alloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
alloc.descriptorPool = frame->descriptorPool;
alloc.pSetLayouts = &descriptorSetLayout_;
alloc.descriptorSetCount = 1;
VkResult res = vkAllocateDescriptorSets(device_, &alloc, &descSet);
assert(VK_SUCCESS == res);
2015-12-20 23:39:03 +01:00
VkDescriptorBufferInfo bufferDesc;
bufferDesc.buffer = buf;
bufferDesc.offset = 0;
bufferDesc.range = curPipeline_->GetUBOSize();
VkDescriptorImageInfo imageDesc;
imageDesc.imageView = boundTextures_[0]->GetImageView();
imageDesc.sampler = boundSamplers_[0]->GetSampler();
imageDesc.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2016-03-21 00:35:27 -07:00
VkWriteDescriptorSet writes[2] = {};
writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[0].dstSet = descSet;
writes[0].dstArrayElement = 0;
2016-01-02 02:08:05 +01:00
writes[0].dstBinding = 0;
writes[0].pBufferInfo = &bufferDesc;
2016-01-03 00:46:41 +01:00
writes[0].pImageInfo = nullptr;
writes[0].pTexelBufferView = nullptr;
writes[0].descriptorCount = 1;
writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[1].dstSet = descSet;
writes[1].dstArrayElement = 0;
2016-01-02 02:08:05 +01:00
writes[1].dstBinding = 1;
2016-01-03 00:46:41 +01:00
writes[1].pBufferInfo = nullptr;
writes[1].pImageInfo = &imageDesc;
2016-01-03 00:46:41 +01:00
writes[1].pTexelBufferView = nullptr;
writes[1].descriptorCount = 1;
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
vkUpdateDescriptorSets(device_, 2, writes, 0, nullptr);
frame->descSets_[key] = descSet;
return descSet;
}
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
2017-05-07 10:53:04 +02:00
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float));
VKInputLayout *input = (VKInputLayout *)desc.inputLayout;
VKBlendState *blend = (VKBlendState *)desc.blend;
VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil;
VKRasterState *raster = (VKRasterState *)desc.raster;
for (int i = 0; i < (int)input->bindings.size(); i++) {
pipeline->stride[i] = input->bindings[i].stride;
}
std::vector<VkPipelineShaderStageCreateInfo> stages;
stages.resize(desc.shaders.size());
int i = 0;
for (auto &iter : desc.shaders) {
VKShaderModule *vkshader = (VKShaderModule *)iter;
VkPipelineShaderStageCreateInfo &stage = stages[i++];
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage.pNext = nullptr;
stage.pSpecializationInfo = nullptr;
stage.stage = StageToVulkan(vkshader->GetStage());
stage.module = vkshader->Get();
stage.pName = "main";
stage.flags = 0;
}
VkPipelineInputAssemblyStateCreateInfo inputAssembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
inputAssembly.topology = primToVK[(int)desc.prim];
inputAssembly.primitiveRestartEnable = false;
VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
dynamicInfo.dynamicStateCount = ARRAY_SIZE(dynamics);
dynamicInfo.pDynamicStates = dynamics;
VkPipelineMultisampleStateCreateInfo ms = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
ms.pNext = nullptr;
ms.pSampleMask = nullptr;
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineViewportStateCreateInfo vs = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
vs.pNext = nullptr;
vs.viewportCount = 1;
vs.scissorCount = 1;
vs.pViewports = nullptr; // dynamic
vs.pScissors = nullptr; // dynamic
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
raster->ToVulkan(&rs);
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
info.pNext = nullptr;
info.flags = 0;
info.stageCount = (uint32_t)stages.size();
info.pStages = stages.data();
2016-12-26 18:32:52 +01:00
info.pColorBlendState = &blend->info;
info.pDepthStencilState = &depth->info;
info.pDynamicState = &dynamicInfo;
info.pInputAssemblyState = &inputAssembly;
info.pTessellationState = nullptr;
info.pMultisampleState = &ms;
info.pVertexInputState = &input->visc;
info.pRasterizationState = &rs;
info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically.
info.layout = pipelineLayout_;
info.subpass = 0;
2016-01-02 02:08:05 +01:00
info.renderPass = vulkan_->GetSurfaceRenderPass();
// OK, need to create a new pipeline.
VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 1, &info, nullptr, &pipeline->vkpipeline);
if (result != VK_SUCCESS) {
ELOG("Failed to create graphics pipeline");
delete pipeline;
return nullptr;
}
if (desc.uniformDesc) {
2017-02-09 13:27:45 +01:00
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
}
return pipeline;
}
2016-12-25 21:21:56 +01:00
void VKContext::SetScissorRect(int left, int top, int width, int height) {
scissor_.offset.x = left;
scissor_.offset.y = top;
scissor_.extent.width = width;
scissor_.extent.height = height;
scissorDirty_ = true;
}
2016-12-25 21:21:56 +01:00
void VKContext::SetViewports(int count, Viewport *viewports) {
if (count > 0) {
viewport_.x = viewports[0].TopLeftX;
viewport_.y = viewports[0].TopLeftY;
viewport_.width = viewports[0].Width;
viewport_.height = viewports[0].Height;
viewport_.minDepth = viewports[0].MinDepth;
viewport_.maxDepth = viewports[0].MaxDepth;
viewportDirty_ = true;
}
}
2016-12-27 15:52:03 +01:00
void VKContext::SetBlendFactor(float color[4]) {
vkCmdSetBlendConstants(cmd_, color);
}
2016-12-25 21:21:56 +01:00
void VKContext::ApplyDynamicState() {
if (scissorDirty_) {
vkCmdSetScissor(cmd_, 0, 1, &scissor_);
scissorDirty_ = false;
}
if (viewportDirty_) {
vkCmdSetViewport(cmd_, 0, 1, &viewport_);
viewportDirty_ = false;
}
}
2016-12-25 21:21:56 +01:00
void VKContext::DirtyDynamicState() {
scissorDirty_ = true;
viewportDirty_ = true;
}
InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) {
VKInputLayout *vl = new VKInputLayout();
vl->visc = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
vl->visc.flags = 0;
vl->visc.vertexAttributeDescriptionCount = (uint32_t)desc.attributes.size();
vl->visc.vertexBindingDescriptionCount = (uint32_t)desc.bindings.size();
vl->bindings.resize(vl->visc.vertexBindingDescriptionCount);
vl->attributes.resize(vl->visc.vertexAttributeDescriptionCount);
vl->visc.pVertexBindingDescriptions = vl->bindings.data();
vl->visc.pVertexAttributeDescriptions = vl->attributes.data();
for (size_t i = 0; i < desc.attributes.size(); i++) {
vl->attributes[i].binding = (uint32_t)desc.attributes[i].binding;
vl->attributes[i].format = DataFormatToVulkan(desc.attributes[i].format);
vl->attributes[i].location = desc.attributes[i].location;
vl->attributes[i].offset = desc.attributes[i].offset;
}
for (size_t i = 0; i < desc.bindings.size(); i++) {
vl->bindings[i].inputRate = desc.bindings[i].instanceRate ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
vl->bindings[i].binding = (uint32_t)i;
vl->bindings[i].stride = desc.bindings[i].stride;
}
return vl;
}
Texture *VKContext::CreateTexture(const TextureDesc &desc) {
return new VKTexture(vulkan_, desc);
}
2016-12-25 21:21:56 +01:00
void VKTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {
VkFormat vulkanFormat = DataFormatToVulkan(format_);
2017-01-18 00:31:44 +07:00
if (stride == 0) {
stride = width * (int)DataFormatSizeInBytes(format_);
2017-01-18 00:31:44 +07:00
}
int bpp = GetBpp(vulkanFormat);
2016-01-03 12:37:05 +01:00
int bytesPerPixel = bpp / 8;
2016-01-10 11:41:46 +01:00
vkTex_->Create(width, height, vulkanFormat);
2016-01-03 12:37:05 +01:00
int rowPitch;
2016-01-10 11:41:46 +01:00
uint8_t *dstData = vkTex_->Lock(0, &rowPitch);
2016-01-03 12:37:05 +01:00
for (int y = 0; y < height; y++) {
memcpy(dstData + rowPitch * y, data + stride * y, width * bytesPerPixel);
}
2016-01-10 11:41:46 +01:00
vkTex_->Unlock();
}
inline void CopySide(VkStencilOpState &dest, const StencilSide &src) {
dest.compareMask = src.compareMask;
dest.reference = src.reference;
dest.writeMask = src.writeMask;
dest.compareOp = compToVK[(int)src.compareOp];
dest.failOp = stencilOpToVK[(int)src.failOp];
dest.passOp = stencilOpToVK[(int)src.passOp];
dest.depthFailOp = stencilOpToVK[(int)src.depthFailOp];
}
2016-12-25 21:21:56 +01:00
DepthStencilState *VKContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
VKDepthStencilState *ds = new VKDepthStencilState();
ds->info.depthCompareOp = compToVK[(int)desc.depthCompare];
ds->info.depthTestEnable = desc.depthTestEnabled;
ds->info.depthWriteEnable = desc.depthWriteEnabled;
ds->info.stencilTestEnable = false;
ds->info.depthBoundsTestEnable = false;
if (ds->info.stencilTestEnable) {
CopySide(ds->info.front, desc.front);
CopySide(ds->info.back, desc.back);
}
return ds;
}
2016-12-25 21:21:56 +01:00
BlendState *VKContext::CreateBlendState(const BlendStateDesc &desc) {
VKBlendState *bs = new VKBlendState();
2016-12-26 18:32:52 +01:00
bs->info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
bs->info.attachmentCount = 1;
bs->info.logicOp = logicOpToVK[(int)desc.logicOp];
bs->info.logicOpEnable = desc.logicEnabled;
bs->attachments.resize(1);
bs->attachments[0].blendEnable = desc.enabled;
bs->attachments[0].colorBlendOp = blendEqToVk[(int)desc.eqCol];
bs->attachments[0].alphaBlendOp = blendEqToVk[(int)desc.eqAlpha];
bs->attachments[0].colorWriteMask = desc.colorMask;
bs->attachments[0].dstAlphaBlendFactor = blendFactorToVk[(int)desc.dstAlpha];
bs->attachments[0].dstColorBlendFactor = blendFactorToVk[(int)desc.dstCol];
bs->attachments[0].srcAlphaBlendFactor = blendFactorToVk[(int)desc.srcAlpha];
bs->attachments[0].srcColorBlendFactor = blendFactorToVk[(int)desc.srcCol];
bs->info.pAttachments = bs->attachments.data();
return bs;
}
// Very simplistic buffer that will simply copy its contents into our "pushbuffer" when it's time to draw,
// to avoid synchronization issues.
class VKBuffer : public Buffer {
public:
VKBuffer(size_t size, uint32_t flags) : dataSize_(size) {
data_ = new uint8_t[size];
}
~VKBuffer() override {
delete[] data_;
}
size_t GetSize() const { return dataSize_; }
const uint8_t *GetData() const { return data_; }
uint8_t *data_;
size_t dataSize_;
};
2016-12-25 21:21:56 +01:00
Buffer *VKContext::CreateBuffer(size_t size, uint32_t usageFlags) {
return new VKBuffer(size, usageFlags);
}
void VKContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) {
VKBuffer *buf = (VKBuffer *)buffer;
memcpy(buf->data_ + offset, data, size);
2017-02-07 18:16:52 +01:00
}
2016-12-25 21:21:56 +01:00
void VKContext::BindTextures(int start, int count, Texture **textures) {
for (int i = start; i < start + count; i++) {
2016-12-25 21:21:56 +01:00
boundTextures_[i] = static_cast<VKTexture *>(textures[i]);
boundImageView_[i] = boundTextures_[i]->GetImageView();
}
}
ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size) {
2016-12-25 22:24:14 +01:00
VKShaderModule *shader = new VKShaderModule(stage);
if (shader->Compile(vulkan_, language, data, size)) {
return shader;
} else {
ELOG("Failed to compile shader: %s", (const char *)data);
shader->Release();
return nullptr;
}
}
int VKPipeline::GetUniformLoc(const char *name) {
int loc = -1;
// HACK! As we only use one uniform we hardcode it.
if (!strcmp(name, "WorldViewProj")) {
return 0;
}
return loc;
}
inline VkPrimitiveTopology PrimToVK(Primitive prim) {
switch (prim) {
case Primitive::POINT_LIST: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case Primitive::LINE_LIST: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case Primitive::LINE_LIST_ADJ: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
case Primitive::LINE_STRIP: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Primitive::LINE_STRIP_ADJ: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
case Primitive::TRIANGLE_LIST: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Primitive::TRIANGLE_LIST_ADJ: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
case Primitive::TRIANGLE_STRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
case Primitive::TRIANGLE_STRIP_ADJ: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
case Primitive::TRIANGLE_FAN: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case Primitive::PATCH_LIST: return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
default:
return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
}
}
void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
curPipeline_->SetDynamicUniformData(ub, size);
}
void VKContext::Draw(int vertexCount, int offset) {
ApplyDynamicState();
VKBuffer *vbuf = curVBuffers_[0];
VkBuffer vulkanVbuf;
VkBuffer vulkanUBObuf;
uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);
2016-03-21 00:35:27 -07:00
size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf);
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline);
VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf);
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset);
VkBuffer buffers[1] = { vulkanVbuf };
VkDeviceSize offsets[1] = { vbBindOffset };
vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets);
vkCmdDraw(cmd_, vertexCount, 1, offset, 0);
}
void VKContext::DrawIndexed(int vertexCount, int offset) {
ApplyDynamicState();
VKBuffer *ibuf = static_cast<VKBuffer *>(curIBuffer_);
VKBuffer *vbuf = static_cast<VKBuffer *>(curVBuffers_[0]);
VkBuffer vulkanVbuf, vulkanIbuf, vulkanUBObuf;
uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);
size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), &vulkanVbuf);
size_t ibBindOffset = push_->Push(ibuf->GetData(), ibuf->GetSize(), &vulkanIbuf);
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline);
VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf);
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset);
VkBuffer buffers[1] = { vulkanVbuf };
VkDeviceSize offsets[1] = { vbBindOffset };
vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets);
vkCmdBindIndexBuffer(cmd_, vulkanIbuf, ibBindOffset, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmd_, vertexCount, 1, 0, offset, 0);
}
void VKContext::DrawUP(const void *vdata, int vertexCount) {
ApplyDynamicState();
VkBuffer vulkanVbuf, vulkanUBObuf;
size_t vbBindOffset = push_->Push(vdata, vertexCount * curPipeline_->stride[0], &vulkanVbuf);
uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);
vkCmdBindPipeline(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, curPipeline_->vkpipeline);
VkBuffer buffers[1] = { vulkanVbuf };
VkDeviceSize offsets[1] = { vbBindOffset };
vkCmdBindVertexBuffers(cmd_, 0, 1, buffers, offsets);
2016-01-03 12:37:05 +01:00
VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf);
2016-01-03 12:37:05 +01:00
vkCmdBindDescriptorSets(cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 1, &ubo_offset);
vkCmdDraw(cmd_, vertexCount, 1, 0, 0);
}
// TODO: We should avoid this function as much as possible, instead use renderpass on-load clearing.
2016-12-25 21:21:56 +01:00
void VKContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) {
if (!curRenderPass_) {
ELOG("Clear: Need an active render pass");
return;
}
int numAttachments = 0;
VkClearRect rc{};
rc.baseArrayLayer = 0;
rc.layerCount = 1;
rc.rect.extent.width = curWidth_;
rc.rect.extent.height = curHeight_;
VkClearAttachment attachments[2];
if (mask & FBChannel::FB_COLOR_BIT) {
VkClearAttachment &attachment = attachments[numAttachments++];
attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
attachment.colorAttachment = 0;
Uint8x4ToFloat4(colorval, attachment.clearValue.color.float32);
}
if (mask & (FBChannel::FB_DEPTH_BIT | FBChannel::FB_STENCIL_BIT)) {
VkClearAttachment &attachment = attachments[numAttachments++];
attachment.aspectMask = 0;
if (mask & FBChannel::FB_DEPTH_BIT) {
attachment.clearValue.depthStencil.depth = depthVal;
attachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (mask & FBChannel::FB_STENCIL_BIT) {
attachment.clearValue.depthStencil.stencil = stencilVal;
attachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
}
if (numAttachments) {
vkCmdClearAttachments(cmd_, numAttachments, attachments, 1, &rc);
}
}
2016-12-25 21:01:57 +01:00
DrawContext *T3DCreateVulkanContext(VulkanContext *vulkan) {
2016-12-25 21:21:56 +01:00
return new VKContext(vulkan);
}
void AddFeature(std::vector<std::string> &features, const char *name, VkBool32 available, VkBool32 enabled) {
char buf[512];
snprintf(buf, sizeof(buf), "%s: Available: %d Enabled: %d", name, (int)available, (int)enabled);
features.push_back(buf);
}
std::vector<std::string> VKContext::GetFeatureList() const {
const VkPhysicalDeviceFeatures &available = vulkan_->GetFeaturesAvailable();
const VkPhysicalDeviceFeatures &enabled = vulkan_->GetFeaturesEnabled();
std::vector<std::string> features;
AddFeature(features, "dualSrcBlend", available.dualSrcBlend, enabled.dualSrcBlend);
AddFeature(features, "logicOp", available.logicOp, enabled.logicOp);
AddFeature(features, "geometryShader", available.geometryShader, enabled.geometryShader);
AddFeature(features, "depthBounds", available.depthBounds, enabled.depthBounds);
AddFeature(features, "depthClamp", available.depthClamp, enabled.depthClamp);
AddFeature(features, "fillModeNonSolid", available.fillModeNonSolid, enabled.fillModeNonSolid);
AddFeature(features, "largePoints", available.largePoints, enabled.largePoints);
AddFeature(features, "wideLines", available.wideLines, enabled.wideLines);
AddFeature(features, "pipelineStatisticsQuery", available.pipelineStatisticsQuery, enabled.pipelineStatisticsQuery);
AddFeature(features, "samplerAnisotropy", available.samplerAnisotropy, enabled.samplerAnisotropy);
AddFeature(features, "textureCompressionBC", available.textureCompressionBC, enabled.textureCompressionBC);
AddFeature(features, "textureCompressionETC2", available.textureCompressionETC2, enabled.textureCompressionETC2);
AddFeature(features, "textureCompressionASTC_LDR", available.textureCompressionASTC_LDR, enabled.textureCompressionASTC_LDR);
AddFeature(features, "shaderClipDistance", available.shaderClipDistance, enabled.shaderClipDistance);
AddFeature(features, "shaderCullDistance", available.shaderCullDistance, enabled.shaderCullDistance);
AddFeature(features, "occlusionQueryPrecise", available.occlusionQueryPrecise, enabled.occlusionQueryPrecise);
AddFeature(features, "multiDrawIndirect", available.multiDrawIndirect, enabled.multiDrawIndirect);
// Also list texture formats and their properties.
for (int i = VK_FORMAT_BEGIN_RANGE; i <= VK_FORMAT_END_RANGE; i++) {
// TODO
}
return features;
}
2016-12-25 18:18:19 +01:00
uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const {
// TODO: Actually do proper checks
switch (fmt) {
case DataFormat::B8G8R8A8_UNORM:
return FMT_RENDERTARGET | FMT_TEXTURE;
case DataFormat::B4G4R4A4_UNORM_PACK16:
// This is the one that's guaranteed to be supported.
// A four-component, 16-bit packed unsigned normalized format that has a 4-bit B component in bits 12..15, a 4-bit
// G component in bits 8..11, a 4 - bit R component in bits 4..7, and a 4 - bit A component in bits 0..3
return FMT_RENDERTARGET | FMT_TEXTURE;
case DataFormat::R4G4B4A4_UNORM_PACK16:
return 0;
case DataFormat::A4R4G4B4_UNORM_PACK16:
return 0;
case DataFormat::R8G8B8A8_UNORM:
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT;
case DataFormat::R32_FLOAT:
case DataFormat::R32G32_FLOAT:
case DataFormat::R32G32B32_FLOAT:
case DataFormat::R32G32B32A32_FLOAT:
return FMT_INPUTLAYOUT;
case DataFormat::R8_UNORM:
return FMT_TEXTURE;
case DataFormat::BC1_RGBA_UNORM_BLOCK:
case DataFormat::BC2_UNORM_BLOCK:
case DataFormat::BC3_UNORM_BLOCK:
return FMT_TEXTURE;
default:
return 0;
}
}
void CreateImage(VulkanContext *vulkan, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) {
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.arrayLayers = 1;
ici.mipLevels = 1;
ici.extent.width = width;
ici.extent.height = height;
ici.extent.depth = 1;
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ici.imageType = VK_IMAGE_TYPE_2D;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.format = format;
ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (color) {
ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
} else {
ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image);
// TODO: If available, use nVidia's VK_NV_dedicated_allocation for framebuffers
VkMemoryRequirements memreq;
vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq);
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
alloc.allocationSize = memreq.size;
vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex);
VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory);
assert(res == VK_SUCCESS);
res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0);
assert(res == VK_SUCCESS);
VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
ivci.format = ici.format;
ivci.image = img.image;
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.subresourceRange.aspectMask = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
ivci.subresourceRange.layerCount = 1;
ivci.subresourceRange.levelCount = 1;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.view);
assert(res == VK_SUCCESS);
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
barrier.image = img.image;
barrier.srcAccessMask = 0;
switch (initialLayout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
}
barrier.newLayout = initialLayout;
barrier.subresourceRange.aspectMask = ivci.subresourceRange.aspectMask;
vkCmdPipelineBarrier(vulkan->GetInitCommandBuffer(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
img.layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
2017-05-23 21:56:48 +02:00
// A VKFramebuffer is a VkFramebuffer (note caps difference) plus all the textures it owns.
// It also has a reference to the command buffer that it was last rendered to with.
// If it needs to be transitioned, and the frame number matches, use it, otherwise
// use this frame's init command buffer.
2017-02-06 01:38:50 +01:00
class VKFramebuffer : public Framebuffer {
public:
VKFramebuffer(VulkanContext *vk) : vulkan_(vk) {}
~VKFramebuffer() {
vulkan_->Delete().QueueDeleteImage(color.image);
vulkan_->Delete().QueueDeleteImage(depth.image);
vulkan_->Delete().QueueDeleteImageView(color.view);
vulkan_->Delete().QueueDeleteImageView(depth.view);
vulkan_->Delete().QueueDeleteDeviceMemory(color.memory);
vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory);
vulkan_->Delete().QueueDeleteFramebuffer(framebuf);
}
2017-05-23 21:56:48 +02:00
VkFramebuffer framebuf = VK_NULL_HANDLE;
VKImage color{};
VKImage depth{};
int width = 0;
int height = 0;
// These belong together, see above.
2017-05-23 21:56:48 +02:00
VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
int frameCount = 0;
private:
VulkanContext *vulkan_;
2017-02-06 01:38:50 +01:00
};
Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {
VKFramebuffer *fb = new VKFramebuffer(vulkan_);
2017-02-06 01:38:50 +01:00
fb->width = desc.width;
fb->height = desc.height;
CreateImage(vulkan_, fb->color, fb->width, fb->height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true);
CreateImage(vulkan_, fb->depth, fb->width, fb->height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false);
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
VkImageView views[2]{};
fbci.renderPass = renderPasses_[0];
fbci.attachmentCount = 2;
fbci.pAttachments = views;
views[0] = fb->color.view;
views[1] = fb->depth.view;
fbci.width = fb->width;
fbci.height = fb->height;
fbci.layers = 1;
vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &fb->framebuf);
2017-02-06 01:38:50 +01:00
return fb;
}
void 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) {
// Can't copy during render passes.
EndCurrentRenderpass();
VKFramebuffer *src = (VKFramebuffer *)srcfb;
VKFramebuffer *dst = (VKFramebuffer *)dstfb;
VkImageCopy copy{};
copy.srcOffset.x = x;
copy.srcOffset.y = y;
copy.srcOffset.z = z;
copy.srcSubresource.mipLevel = level;
copy.srcSubresource.layerCount = 1;
copy.dstOffset.x = dstX;
copy.dstOffset.y = dstY;
copy.dstOffset.z = dstZ;
copy.dstSubresource.mipLevel = dstLevel;
copy.dstSubresource.layerCount = 1;
copy.extent.width = width;
copy.extent.height = height;
copy.extent.depth = depth;
// We're gonna tack copies onto the src's command buffer, if it's from this frame.
// If from a previous frame, just do it in frame init.
VkCommandBuffer cmd = src->cmdBuf;
if (src->frameCount != frameNum_) {
2017-05-24 00:45:15 +02:00
// TODO: What about the case where dst->frameCount == frameNum_ here?
// That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest.
cmd = vulkan_->GetInitCommandBuffer();
}
VkImageMemoryBarrier srcBarriers[2]{};
VkImageMemoryBarrier dstBarriers[2]{};
int srcCount = 0;
int dstCount = 0;
// First source barriers.
if (channelBits & FB_COLOR_BIT) {
if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT);
}
if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT);
}
}
// We can't copy only depth or only stencil unfortunately.
if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) {
if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
}
if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
}
}
// TODO: Fix the pipe bits to be bit less conservative.
if (srcCount) {
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers);
}
if (dstCount) {
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers);
}
if (channelBits & FB_COLOR_BIT) {
copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
vkCmdCopyImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &copy);
}
if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) {
copy.srcSubresource.aspectMask = 0;
copy.dstSubresource.aspectMask = 0;
if (channelBits & FB_DEPTH_BIT) {
copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (channelBits & FB_STENCIL_BIT) {
copy.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
copy.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
vkCmdCopyImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &copy);
}
}
2017-05-24 00:45:15 +02:00
bool 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) {
VKFramebuffer *src = (VKFramebuffer *)srcfb;
VKFramebuffer *dst = (VKFramebuffer *)dstfb;
// We're gonna tack blits onto the src's command buffer, if it's from this frame.
// If from a previous frame, just do it in frame init.
VkCommandBuffer cmd = src->cmdBuf;
if (src->frameCount != frameNum_) {
// TODO: What about the case where dst->frameCount == frameNum_ here?
// That will cause bad ordering. We'll have to allocate a new command buffer and assign it to dest.
cmd = vulkan_->GetInitCommandBuffer();
}
VkImageMemoryBarrier srcBarriers[2]{};
VkImageMemoryBarrier dstBarriers[2]{};
int srcCount = 0;
int dstCount = 0;
VkImageBlit blit{};
blit.srcOffsets[0].x = srcX1;
blit.srcOffsets[0].y = srcY1;
blit.srcOffsets[0].z = 0;
blit.srcOffsets[1].x = srcX2;
blit.srcOffsets[1].y = srcY2;
blit.srcOffsets[1].z = 1;
blit.srcSubresource.mipLevel = 0;
blit.srcSubresource.layerCount = 1;
blit.dstOffsets[0].x = dstX1;
blit.dstOffsets[0].y = dstY1;
blit.dstOffsets[0].z = 0;
blit.dstOffsets[1].x = dstX2;
blit.dstOffsets[1].y = dstY2;
blit.dstOffsets[1].z = 1;
blit.dstSubresource.mipLevel = 0;
blit.dstSubresource.layerCount = 1;
// First source barriers.
if (channelBits & FB_COLOR_BIT) {
if (src->color.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
SetupTransitionToTransferSrc(src->color, srcBarriers[srcCount++], VK_IMAGE_ASPECT_COLOR_BIT);
}
if (dst->color.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
SetupTransitionToTransferDst(dst->color, dstBarriers[dstCount++], VK_IMAGE_ASPECT_COLOR_BIT);
}
}
// We can't copy only depth or only stencil unfortunately.
if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) {
if (src->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) {
SetupTransitionToTransferSrc(src->depth, srcBarriers[srcCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
}
if (dst->depth.layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
SetupTransitionToTransferDst(dst->depth, dstBarriers[dstCount++], VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
}
}
// TODO: Fix the pipe bits to be bit less conservative.
if (srcCount) {
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, srcCount, srcBarriers);
}
if (dstCount) {
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, dstCount, dstBarriers);
}
if (channelBits & FB_COLOR_BIT) {
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
vkCmdBlitImage(cmd, src->color.image, src->color.layout, dst->color.image, dst->color.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
}
if (channelBits & (FB_DEPTH_BIT | FB_STENCIL_BIT)) {
blit.srcSubresource.aspectMask = 0;
blit.dstSubresource.aspectMask = 0;
if (channelBits & FB_DEPTH_BIT) {
blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (channelBits & FB_STENCIL_BIT) {
blit.srcSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
blit.dstSubresource.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
2017-05-24 00:45:15 +02:00
vkCmdBlitImage(cmd, src->depth.image, src->depth.layout, dst->depth.image, dst->depth.layout, 1, &blit, filter == FB_BLIT_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
}
return true;
}
void VKContext::SetupTransitionToTransferSrc(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) {
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = img.layout;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
barrier.image = img.image;
barrier.srcAccessMask = 0;
switch (img.layout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
2017-05-24 00:45:15 +02:00
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
default:
Crash();
}
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.subresourceRange.aspectMask = aspect;
img.layout = barrier.newLayout;
}
void VKContext::SetupTransitionToTransferDst(VKImage &img, VkImageMemoryBarrier &barrier, VkImageAspectFlags aspect) {
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = img.layout;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
barrier.image = img.image;
barrier.srcAccessMask = 0;
switch (img.layout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
2017-05-24 00:45:15 +02:00
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
default:
Crash();
}
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.subresourceRange.aspectMask = aspect;
img.layout = barrier.newLayout;
}
2017-02-06 01:38:50 +01:00
void VKContext::EndCurrentRenderpass() {
if (curRenderPass_ != VK_NULL_HANDLE) {
vkCmdEndRenderPass(cmd_);
curRenderPass_ = VK_NULL_HANDLE;
curFramebuffer_ = VK_NULL_HANDLE;
cmd_ = VK_NULL_HANDLE;
}
}
void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) {
VkFramebuffer framebuf;
VkCommandBuffer cmdBuf;
int w;
int h;
VkImageLayout prevLayout;
if (fbo) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
framebuf = fb->framebuf;
w = fb->width;
h = fb->height;
prevLayout = fb->color.layout;
cmdBuf = fb->cmdBuf;
} else {
framebuf = vulkan_->GetSurfaceFramebuffer();
w = vulkan_->GetWidth();
h = vulkan_->GetHeight();
cmdBuf = vulkan_->GetSurfaceCommandBuffer();
}
if (framebuf == curFramebuffer_) {
if (framebuf == 0)
Crash();
if (!curRenderPass_)
Crash();
// If we're asking to clear, but already bound, we'll just keep it bound but send a clear command.
2017-05-23 21:56:48 +02:00
// We will try to avoid this as much as possible.
VkClearAttachment clear[2]{};
int count = 0;
if (rp.color == RPAction::CLEAR) {
2017-05-23 21:56:48 +02:00
clear[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
Uint8x4ToFloat4(rp.clearColor, clear[count].clearValue.color.float32);
clear[count].colorAttachment = 0;
count++;
}
if (rp.depth == RPAction::CLEAR) {
2017-05-23 21:56:48 +02:00
clear[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
clear[count].clearValue.depthStencil.depth = rp.clearDepth;
clear[count].clearValue.depthStencil.stencil = rp.clearStencil;
clear[count].colorAttachment = 0;
count++;
}
if (count > 0) {
VkClearRect rc{ { 0,0,(uint32_t)w,(uint32_t)h }, 0, 1 };
2017-05-23 21:56:48 +02:00
vkCmdClearAttachments(cmdBuf, count, clear, 1, &rc);
}
// We're done.
return;
}
// OK, we're switching framebuffers.
EndCurrentRenderpass();
VkRenderPass renderPass;
int numClearVals = 0;
2017-05-22 15:29:14 +02:00
VkClearValue clearVal[2];
memset(clearVal, 0, sizeof(clearVal));
if (fbo) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
fb->cmdBuf = AllocCmdBuf();
if (!fb->cmdBuf)
Crash();
fb->frameCount = frameNum_;
cmd_ = fb->cmdBuf;
VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VkResult res = vkBeginCommandBuffer(cmd_, &begin);
assert(res == VK_SUCCESS);
// Now, if the image needs transitioning, let's transition.
// The backbuffer does not, that's handled by VulkanContext.
2017-05-23 10:26:49 +02:00
if (fb->color.layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = fb->color.layout;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
barrier.image = fb->color.image;
barrier.srcAccessMask = 0;
switch (fb->color.layout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
2017-05-24 00:45:15 +02:00
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
2017-05-23 10:26:49 +02:00
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
break;
}
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
2017-05-23 10:26:49 +02:00
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2017-05-23 21:56:48 +02:00
// TODO: Double-check these flags. Should be fine.
vkCmdPipelineBarrier(cmd_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
2017-05-23 10:26:49 +02:00
fb->color.layout = barrier.newLayout;
}
if (fb->depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = fb->depth.layout;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
barrier.image = fb->depth.image;
barrier.srcAccessMask = 0;
switch (fb->depth.layout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
}
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
// TODO: Double-check these flags. Should be fine.
vkCmdPipelineBarrier(cmd_, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
fb->depth.layout = barrier.newLayout;
}
renderPass = renderPasses_[RPIndex(rp.color, rp.depth)];
// ILOG("Switching framebuffer to FBO (fc=%d, cmd=%x, rp=%x)", frameNum_, (int)(uintptr_t)cmd_, (int)(uintptr_t)renderPass);
if (rp.color == RPAction::CLEAR) {
Uint8x4ToFloat4(rp.clearColor, clearVal[0].color.float32);
numClearVals = 1;
}
if (rp.depth == RPAction::CLEAR) {
clearVal[1].depthStencil.depth = rp.clearDepth;
clearVal[1].depthStencil.stencil = rp.clearStencil;
numClearVals = 2;
}
} else {
cmd_ = vulkan_->GetSurfaceCommandBuffer();
renderPass = vulkan_->GetSurfaceRenderPass();
numClearVals = 2;
}
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
rp_begin.renderPass = renderPass;
rp_begin.framebuffer = framebuf;
rp_begin.renderArea.offset.x = 0;
rp_begin.renderArea.offset.y = 0;
rp_begin.renderArea.extent.width = w;
rp_begin.renderArea.extent.height = h;
rp_begin.clearValueCount = numClearVals;
rp_begin.pClearValues = numClearVals ? clearVal : nullptr;
vkCmdBeginRenderPass(cmd_, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
curFramebuffer_ = framebuf;
curRenderPass_ = renderPass;
curWidth_ = w;
curHeight_ = h;
}
// color must be 0, for now.
void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
2017-05-23 10:26:49 +02:00
boundImageView_[0] = fb->color.view;
// If we already have the right layout, nothing else to do.
if (fb->color.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
return;
VkCommandBuffer transitionCmdBuf;
if (fb->cmdBuf && fb->frameCount == frameNum_) {
// If the framebuffer has a "live" command buffer, we can directly use it to transition it for sampling.
transitionCmdBuf = fb->cmdBuf;
} else {
// If not, we can just do it at the "start" of the frame.
transitionCmdBuf = vulkan_->GetInitCommandBuffer();
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = fb->color.layout;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
barrier.image = fb->color.image;
barrier.srcAccessMask = 0;
switch (barrier.oldLayout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT|VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
}
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2017-05-23 10:26:49 +02:00
// we're between passes so it's OK.
2017-05-23 10:26:49 +02:00
// ARM Best Practices guide recommends these stage bits.
vkCmdPipelineBarrier(transitionCmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
fb->color.layout = barrier.newLayout;
}
void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ }
uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {
// TODO: Insert transition at the end of the previous command buffer, or the one that rendered to it last.
VKFramebuffer *fb = (VKFramebuffer *)fbo;
return (uintptr_t)fb->color.image;
}
2017-02-06 01:38:50 +01:00
void VKContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
2017-02-06 01:38:50 +01:00
VKFramebuffer *fb = (VKFramebuffer *)fbo;
*w = fb->width;
*h = fb->height;
}
void VKContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) {
// Noop
}
} // namespace Draw