More progress

This commit is contained in:
Henrik Rydgard committed 2016-03-20 19:26:41 +01:00
1 parent 4ddca8607f
commit cfbecf5071
18 files changed
+320 -426

No files matched your search

+147 -102
View File
@@ -42,7 +42,7 @@ VulkanContext::VulkanContext(const char *app_name, uint32_t flags)
: device_(nullptr),
gfx_queue_(nullptr),
connection(nullptr),
gfx_queue_family_index_(-1),
graphics_queue_family_index_(-1),
surface(nullptr),
window(nullptr),
prepared(false),
@@ -50,6 +50,7 @@ VulkanContext::VulkanContext(const char *app_name, uint32_t flags)
instance_(nullptr),
width(0),
height(0),
flags_(flags),
swapchain_format(VK_FORMAT_UNDEFINED),
swapchainImageCount(0),
swap_chain_(nullptr),
@@ -58,7 +59,8 @@ VulkanContext::VulkanContext(const char *app_name, uint32_t flags)
cmdInitActive_(false),
dbgCreateMsgCallback(nullptr),
dbgDestroyMsgCallback(nullptr),
queue_count(0)
queue_count(0),
curFrame_(0)
{
// List extensions to try to enable.
instance_extension_names.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
@@ -117,14 +119,14 @@ VulkanContext::VulkanContext(const char *app_name, uint32_t flags)
res = vkEnumeratePhysicalDevices(instance_, &gpu_count, physical_devices_.data());
assert(!res);
init_global_layer_properties();
init_global_extension_properties();
InitGlobalLayerProperties();
InitGlobalExtensionProperties();
if (!CheckLayers(instance_layer_properties, instance_layer_names)) {
exit(1);
}
init_device_layer_properties();
InitDeviceLayerProperties();
if (!CheckLayers(device_layer_properties, device_layer_names)) {
exit(1);
}
@@ -134,7 +136,7 @@ VulkanContext::~VulkanContext() {
vkDestroyInstance(instance_, NULL);
}
void vk_transition_to_present(VkCommandBuffer cmd, VkImage image) {
void TransitionToPresent(VkCommandBuffer cmd, VkImage image) {
VkImageMemoryBarrier prePresentBarrier = {};
prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
prePresentBarrier.pNext = NULL;
@@ -154,45 +156,39 @@ void vk_transition_to_present(VkCommandBuffer cmd, VkImage image) {
0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier);
}
void vk_transition_from_present(VkCommandBuffer cmd, VkImage image) {
VkImageMemoryBarrier postPresentBarrier = {};
postPresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
postPresentBarrier.pNext = NULL;
postPresentBarrier.srcAccessMask = 0;
postPresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
postPresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
postPresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
postPresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
postPresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
postPresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
postPresentBarrier.subresourceRange.baseMipLevel = 0;
postPresentBarrier.subresourceRange.levelCount = 1;
postPresentBarrier.subresourceRange.baseArrayLayer = 0;
postPresentBarrier.subresourceRange.layerCount = 1;
postPresentBarrier.image = image;
void TransitionFromPresent(VkCommandBuffer cmd, VkImage image) {
VkImageMemoryBarrier prePresentBarrier = {};
prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
prePresentBarrier.pNext = NULL;
prePresentBarrier.srcAccessMask = 0;
prePresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
prePresentBarrier.subresourceRange.baseMipLevel = 0;
prePresentBarrier.subresourceRange.levelCount = 1;
prePresentBarrier.subresourceRange.baseArrayLayer = 0;
prePresentBarrier.subresourceRange.layerCount = 1;
prePresentBarrier.image = image;
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &postPresentBarrier);
0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier);
}
VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[2]) {
VkSemaphoreCreateInfo acquireSemaphoreCreateInfo;
acquireSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
acquireSemaphoreCreateInfo.pNext = NULL;
acquireSemaphoreCreateInfo.flags = 0;
FrameData *frame = &frame_[curFrame_];
VkResult res = vkCreateSemaphore(device_,
&acquireSemaphoreCreateInfo,
NULL,
&acquireSemaphore);
assert(res == VK_SUCCESS);
// Make sure the command buffer from the frame before the previous has been fully executed.
WaitAndResetFence(frame->fence);
// Get the index of the next available swapchain image, and a semaphore to block on.
res = fpAcquireNextImageKHR(device_, swap_chain_,
// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.
// Now, I wonder if we should do this early in the frame or late?
VkResult res = fpAcquireNextImageKHR(device_, swap_chain_,
UINT64_MAX,
acquireSemaphore,
NULL,
&current_buffer);
// TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
// return codes
assert(res == VK_SUCCESS);
@@ -202,9 +198,9 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
begin.pNext = NULL;
begin.flags = 0;
begin.pInheritanceInfo = nullptr;
vkBeginCommandBuffer(cmd_, &begin);
res = vkBeginCommandBuffer(cmd_, &begin);
vk_transition_from_present(cmd_, swapChainBuffers[current_buffer].image);
TransitionFromPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image);
VkRenderPassBeginInfo rp_begin;
rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
@@ -218,8 +214,8 @@ VkCommandBuffer VulkanContext::BeginSurfaceRenderPass(VkClearValue clear_values[
rp_begin.clearValueCount = 2;
rp_begin.pClearValues = clear_values;
vkCmdBeginRenderPass(cmd_, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
return cmd_;
vkCmdBeginRenderPass(frame->cmdBuf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
return frame->cmdBuf;
}
void VulkanContext::WaitUntilQueueIdle() {
@@ -274,6 +270,47 @@ void vk_submit_sync(VkDevice device, VkSemaphore waitForSemaphore, VkQueue queue
vkDestroyFence(device, drawFence, NULL);
}
void VulkanContext::EndSurfaceRenderPass() {
FrameData *frame = &frame_[curFrame_];
vkCmdEndRenderPass(frame->cmdBuf);
TransitionToPresent(frame->cmdBuf, swapChainBuffers[current_buffer].image);
VkResult res = vkEndCommandBuffer(frame->cmdBuf);
assert(res == VK_SUCCESS);
VkSubmitInfo submit_info = {};
submit_info.pNext = NULL;
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = &acquireSemaphore;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &frame->cmdBuf;
submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = NULL;
res = vkQueueSubmit(gfx_queue_, 1, &submit_info, frame->fence);
assert(res == VK_SUCCESS);
// At this point we are certain that acquireSemaphore is of no further use and can be destroyed.
VkPresentInfoKHR present;
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present.pNext = NULL;
present.swapchainCount = 1;
present.pSwapchains = &swap_chain_;
present.pImageIndices = &current_buffer;
present.pWaitSemaphores = NULL;
present.waitSemaphoreCount = 0;
present.pResults = NULL;
res = fpQueuePresentKHR(gfx_queue_, &present);
// TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI
// return codes
assert(!res);
curFrame_ ^= 1;
}
void VulkanContext::BeginInitCommandBuffer() {
assert(!cmdInitActive_);
VulkanBeginCommandBuffer(cmd_);
@@ -300,9 +337,33 @@ void VulkanContext::InitObjects(HINSTANCE hInstance, HWND hWnd, bool depthPresen
InitSurfaceRenderPass(depthPresent, true);
InitFramebuffers(depthPresent);
SubmitInitCommandBufferSync();
// Create frame data
VkCommandBufferAllocateInfo cmd = {};
cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmd.pNext = NULL;
cmd.commandPool = cmd_pool_;
cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd.commandBufferCount = 2;
VkCommandBuffer cmdBuf[2];
VkResult res = vkAllocateCommandBuffers(device_, &cmd, cmdBuf);
assert(res == VK_SUCCESS);
frame_[0].cmdBuf = cmdBuf[0];
frame_[0].fence = CreateFence(true); // So it can be instantly waited on
frame_[1].cmdBuf = cmdBuf[1];
frame_[1].fence = CreateFence(true);
}
void VulkanContext::DestroyObjects() {
VkCommandBuffer cmdBuf[2] = { frame_[0].cmdBuf, frame_[1].cmdBuf };
vkFreeCommandBuffers(device_, cmd_pool_, 2, cmdBuf);
vkDestroyFence(device_, frame_[0].fence, nullptr);
vkDestroyFence(device_, frame_[1].fence, nullptr);
DestroyFramebuffers();
DestroySurfaceRenderPass();
DestroyDepthStencilBuffer();
@@ -311,39 +372,7 @@ void VulkanContext::DestroyObjects() {
DestroyCommandPool();
}
void VulkanContext::EndSurfaceRenderPass() {
vkCmdEndRenderPass(cmd_);
vk_transition_to_present(cmd_, swapChainBuffers[current_buffer].image);
VkResult res = vkEndCommandBuffer(cmd_);
assert(res == VK_SUCCESS);
/* Make sure command buffer is finished before presenting */
vk_submit_sync(device_, acquireSemaphore, gfx_queue_, cmd_);
// At this point we are certain that acquireSemaphore is of no further use and can be destroyed.
/* Now present the image in the window */
VkPresentInfoKHR present;
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present.pNext = NULL;
present.swapchainCount = 1;
present.pSwapchains = &swap_chain_;
present.pImageIndices = &current_buffer;
present.pWaitSemaphores = NULL;
present.waitSemaphoreCount = 0;
present.pResults = NULL;
res = fpQueuePresentKHR(gfx_queue_, &present);
// TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI
// return codes
assert(!res);
vkDestroySemaphore(device_, acquireSemaphore, NULL);
}
VkResult VulkanContext::init_layer_extension_properties(layer_properties &layer_props) {
VkResult VulkanContext::InitLayerExtensionProperties(layer_properties &layer_props) {
VkExtensionProperties *instance_extensions;
uint32_t instance_extension_count;
VkResult res;
@@ -371,7 +400,7 @@ VkResult VulkanContext::init_layer_extension_properties(layer_properties &layer_
return res;
}
VkResult VulkanContext::init_global_extension_properties() {
VkResult VulkanContext::InitGlobalExtensionProperties() {
uint32_t instance_extension_count;
VkResult res;
@@ -394,7 +423,7 @@ VkResult VulkanContext::init_global_extension_properties() {
return res;
}
VkResult VulkanContext::init_global_layer_properties() {
VkResult VulkanContext::InitGlobalLayerProperties() {
uint32_t instance_layer_count;
VkLayerProperties *vk_props = NULL;
VkResult res;
@@ -429,7 +458,7 @@ VkResult VulkanContext::init_global_layer_properties() {
for (uint32_t i = 0; i < instance_layer_count; i++) {
layer_properties layer_props;
layer_props.properties = vk_props[i];
res = init_layer_extension_properties(layer_props);
res = InitLayerExtensionProperties(layer_props);
if (res)
return res;
instance_layer_properties.push_back(layer_props);
@@ -439,7 +468,7 @@ VkResult VulkanContext::init_global_layer_properties() {
return res;
}
VkResult VulkanContext::init_device_extension_properties(layer_properties &layer_props) {
VkResult VulkanContext::InitDeviceExtensionProperties(layer_properties &layer_props) {
VkExtensionProperties *device_extensions;
uint32_t device_extension_count;
VkResult res;
@@ -469,10 +498,7 @@ VkResult VulkanContext::init_device_extension_properties(layer_properties &layer
return res;
}
/*
* TODO: function description here
*/
VkResult VulkanContext::init_device_layer_properties() {
VkResult VulkanContext::InitDeviceLayerProperties() {
uint32_t device_layer_count;
VkLayerProperties *vk_props = NULL;
VkResult res;
@@ -509,7 +535,7 @@ VkResult VulkanContext::init_device_layer_properties() {
for (uint32_t i = 0; i < device_layer_count; i++) {
layer_properties layer_props;
layer_props.properties = vk_props[i];
res = init_device_extension_properties(layer_props);
res = InitDeviceExtensionProperties(layer_props);
if (res)
return res;
device_layer_properties.push_back(layer_props);
@@ -607,7 +633,6 @@ VkResult VulkanContext::InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFun
std::cout << "GetInstanceProcAddr: Unable to find vkDbgCreateMsgCallback function." << std::endl;
return VK_ERROR_INITIALIZATION_FAILED;
}
std::cout << "Got dbgCreateMsgCallback function\n";
dbgDestroyMsgCallback = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance_, "vkDestroyDebugReportCallbackEXT");
if (!dbgDestroyMsgCallback) {
@@ -624,7 +649,6 @@ VkResult VulkanContext::InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFun
res = dbgCreateMsgCallback(instance_, &cb, nullptr, &msg_callback);
switch (res) {
case VK_SUCCESS:
puts("Successfully created message callback object\n");
msg_callbacks.push_back(msg_callback);
break;
case VK_ERROR_OUT_OF_HOST_MEMORY:
@@ -805,7 +829,7 @@ void VulkanContext::InitSurfaceAndQueue(HINSTANCE conn, HWND wnd) {
exit(-1);
}
gfx_queue_family_index_ = graphicsQueueNodeIndex;
graphics_queue_family_index_ = graphicsQueueNodeIndex;
// Get the list of VkFormats that are supported:
uint32_t formatCount;
@@ -829,7 +853,18 @@ void VulkanContext::InitSurfaceAndQueue(HINSTANCE conn, HWND wnd) {
}
delete[] surfFormats;
vkGetDeviceQueue(device_, gfx_queue_family_index_, 0, &gfx_queue_);
vkGetDeviceQueue(device_, graphics_queue_family_index_, 0, &gfx_queue_);
VkSemaphoreCreateInfo acquireSemaphoreCreateInfo;
acquireSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
acquireSemaphoreCreateInfo.pNext = NULL;
acquireSemaphoreCreateInfo.flags = 0;
res = vkCreateSemaphore(device_,
&acquireSemaphoreCreateInfo,
NULL,
&acquireSemaphore);
assert(res == VK_SUCCESS);
}
void VulkanContext::InitSwapchain() {
@@ -874,13 +909,13 @@ void VulkanContext::InitSwapchain() {
// always available.
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
for (size_t i = 0; i < presentModeCount; i++) {
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
if ((flags_ & VULKAN_FLAG_PRESENT_MAILBOX) && presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
}
if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) &&
(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
if ((flags_ & VULKAN_FLAG_PRESENT_IMMEDIATE) && presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) {
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
break;
}
}
@@ -1063,7 +1098,7 @@ void VulkanContext::InitCommandPool() {
VkCommandPoolCreateInfo cmd_pool_info = {};
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmd_pool_info.pNext = NULL;
cmd_pool_info.queueFamilyIndex = gfx_queue_family_index_;
cmd_pool_info.queueFamilyIndex = graphics_queue_family_index_;
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
res = vkCreateCommandPool(device_, &cmd_pool_info, NULL, &cmd_pool_);
@@ -1295,7 +1330,7 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
// that could be avoided with smarter code - for example we can check the fence the next
// time the texture is used and discard the staging image then.
VkFence fence = vulkan->CreateFence();
VkFence fence = vulkan->CreateFence(false);
res = vkQueueSubmit(vulkan->gfx_queue_, 1, submit_info, fence);
assert(res == VK_SUCCESS);
@@ -1307,7 +1342,7 @@ void VulkanTexture::Unlock(VulkanContext *vulkan) {
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
imageLayout);
vulkan->WaitForFence(fence);
vulkan->WaitAndResetFence(fence);
/* Release the resources for the staging image */
vkFreeMemory(vulkan->device_, mappableMemory, NULL);
@@ -1346,18 +1381,21 @@ void VulkanTexture::Destroy(VulkanContext *vulkan) {
mem = NULL;
}
VkFence VulkanContext::CreateFence() {
VkFence VulkanContext::CreateFence(bool presignalled) {
VkFence fence;
VkFenceCreateInfo fenceInfo;
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.pNext = NULL;
fenceInfo.flags = 0;
fenceInfo.flags = presignalled ? VK_FENCE_CREATE_SIGNALED_BIT : 0;
vkCreateFence(device_, &fenceInfo, NULL, &fence);
return fence;
}
void VulkanContext::WaitForFence(VkFence fence) {
vkWaitForFences(device_, 1, &fence, true, 0);
void VulkanContext::WaitAndResetFence(VkFence fence) {
VkResult res = vkWaitForFences(device_, 1, &fence, true, UINT64_MAX);
assert(!res);
res = vkResetFences(device_, 1, &fence);
assert(!res);
}
void VulkanContext::DestroyCommandBuffer() {
@@ -1387,6 +1425,7 @@ void VulkanContext::DestroySwapChain() {
fpDestroySwapchainKHR(device_, swap_chain_, NULL);
swap_chain_ = nullptr;
swapChainBuffers.clear();
vkDestroySemaphore(device_, acquireSemaphore, NULL);
}
void VulkanContext::DestroyFramebuffers() {
@@ -1426,7 +1465,6 @@ void TransitionImageLayout(
image_memory_barrier.subresourceRange.baseMipLevel = 0;
image_memory_barrier.subresourceRange.levelCount = 1;
image_memory_barrier.subresourceRange.layerCount = 1;
if (old_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
image_memory_barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
}
@@ -1442,7 +1480,9 @@ void TransitionImageLayout(
if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
/* Make sure any Copy or CPU writes to image are flushed */
image_memory_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
if (old_image_layout != VK_IMAGE_LAYOUT_UNDEFINED) {
image_memory_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
}
image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
}
@@ -1580,13 +1620,11 @@ EShLanguage FindLanguage(const VkShaderStageFlagBits shader_type) {
}
}
//
// Compile a given string containing GLSL into SPV for use by VK
// Return value of false means an error was encountered.
//
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type,
const char *pshader,
std::vector<unsigned int> &spirv) {
std::vector<unsigned int> &spirv, std::string *errorMessage) {
glslang::TProgram& program = *new glslang::TProgram;
const char *shaderStrings[1];
@@ -1605,6 +1643,10 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type,
if (!shader->parse(&Resources, 100, false, messages)) {
puts(shader->getInfoLog());
puts(shader->getInfoDebugLog());
if (errorMessage) {
*errorMessage = shader->getInfoLog();
(*errorMessage) += shader->getInfoDebugLog();
}
return false; // something didn't work
}
@@ -1617,11 +1659,15 @@ bool GLSLtoSPV(const VkShaderStageFlagBits shader_type,
if (!program.link(messages)) {
puts(shader->getInfoLog());
puts(shader->getInfoDebugLog());
if (errorMessage) {
*errorMessage = shader->getInfoLog();
(*errorMessage) += shader->getInfoDebugLog();
}
return false;
}
// Can't fail, parsing worked, "linking" worked.
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
return true;
}
@@ -1663,5 +1709,4 @@ const char *VulkanResultToString(VkResult res) {
void VulkanAssertImpl(VkResult check, const char *function, const char *file, int line) {
const char *error = "(none)";
}
+28 -21
View File
@@ -54,6 +54,8 @@
enum {
VULKAN_FLAG_VALIDATE = 1,
VULKAN_FLAG_PRESENT_MAILBOX = 2,
VULKAN_FLAG_PRESENT_IMMEDIATE = 4,
};
// A layer can expose extensions, keep track of those extensions here.
@@ -103,8 +105,8 @@ public:
void WaitUntilQueueIdle();
// Utility functions for shorter code
VkFence CreateFence();
void WaitForFence(VkFence fence);
VkFence CreateFence(bool presignalled);
void WaitAndResetFence(VkFence fence);
int GetWidth() { return width; }
int GetHeight() { return height; }
@@ -120,32 +122,33 @@ public:
VkResult InitDebugMsgCallback(PFN_vkDebugReportCallbackEXT dbgFunc, int bits, void *userdata = nullptr);
void DestroyDebugMsgCallback();
VkSemaphore acquireSemaphore;
VkRenderPass GetSurfaceRenderPass() {
VkRenderPass GetSurfaceRenderPass() const {
return surface_render_pass_;
}
VkPhysicalDevice GetPhysicalDevice() {
VkPhysicalDevice GetPhysicalDevice() const {
return physical_devices_[0];
}
VkQueue GetGraphicsQueue() {
VkQueue GetGraphicsQueue() const {
return gfx_queue_;
}
int GetGraphicsQueueFamilyIndex() {
return gfx_queue_family_index_;
int GetGraphicsQueueFamilyIndex() const {
return graphics_queue_family_index_;
}
VkResult init_global_extension_properties();
VkResult init_layer_extension_properties(layer_properties &layer_props);
VkResult init_global_layer_properties();
VkResult InitGlobalExtensionProperties();
VkResult InitLayerExtensionProperties(layer_properties &layer_props);
VkResult init_device_extension_properties(layer_properties &layer_props);
VkResult init_device_layer_properties();
VkResult InitGlobalLayerProperties();
VkResult InitDeviceExtensionProperties(layer_properties &layer_props);
VkResult InitDeviceLayerProperties();
VkSemaphore acquireSemaphore;
#ifdef _WIN32
#define APP_NAME_STR_LEN 80
@@ -182,7 +185,7 @@ public:
std::vector<VkExtensionProperties> device_extension_properties;
std::vector<VkPhysicalDevice> physical_devices_;
uint32_t gfx_queue_family_index_;
uint32_t graphics_queue_family_index_;
VkPhysicalDeviceProperties gpu_props;
std::vector<VkQueueFamilyProperties> queue_props;
VkPhysicalDeviceMemoryProperties memory_properties;
@@ -195,12 +198,21 @@ private:
// Swap chain
int width, height;
int flags_;
VkFormat swapchain_format;
std::vector<VkFramebuffer> framebuffers_;
uint32_t swapchainImageCount;
VkSwapchainKHR swap_chain_;
std::vector<swap_chain_buffer> swapChainBuffers;
// Manages flipping command buffers for the backbuffer render pass.
// It is recommended to do the same for other rendering passes.
struct FrameData {
VkFence fence;
VkCommandBuffer cmdBuf;
};
FrameData frame_[2];
int curFrame_;
// Simple loader for the WSI extension.
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
@@ -265,7 +277,7 @@ void vk_submit_sync(VkDevice device, VkSemaphore waitForSemaphore, VkQueue queue
void init_glslang();
void finalize_glslang();
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<unsigned int> &spirv);
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<uint32_t> &spirv, std::string *errorMessage = nullptr);
void TransitionImageLayout(
VkCommandBuffer cmd,
@@ -274,10 +286,5 @@ void TransitionImageLayout(
VkImageLayout old_image_layout,
VkImageLayout new_image_layout);
void VulkanAssertImpl(VkResult check, const char *function, const char *file, int line);
// DO NOT call vulkan functions within this! Instead, store the result in a variable and check that.
#define VulkanAssert(x) if ((x) != VK_SUCCESS) VulkanAssertImpl((x), __FUNCTION__, __FILE__, __LINE__);
#endif // UTIL_INIT
+3 -3
View File
@@ -34,7 +34,7 @@ static const char * const vulkan_fsTexCol =
"layout(location = 0) in vec4 oColor0;\n"
"layout(location = 1) in vec2 oTexCoord0;\n"
"layout(location = 0) out vec4 fragColor0\n;"
"layout(binding = 2) uniform sampler2D Sampler0;\n"
"layout(set = 0, binding = 1) uniform sampler2D Sampler0;\n"
"void main() { fragColor0 = texture(Sampler0, oTexCoord0) * oColor0; }\n";
static const char * const glsl_fsCol =
@@ -87,7 +87,7 @@ static const char * const vulkan_vsCol =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"layout (std140, binding = 1) uniform bufferVals {\n"
"layout (std140, set = 0, binding = 0) uniform bufferVals {\n"
" mat4 WorldViewProj;\n"
"} myBufferVals;\n"
"layout (location = 0) in vec4 pos;\n"
@@ -128,7 +128,7 @@ static const char * const vulkan_vsTexCol =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"layout (std140, binding = 1) uniform bufferVals {\n"
"layout (std140, set = 0, binding = 0) uniform bufferVals {\n"
" mat4 WorldViewProj;\n"
"} myBufferVals;\n"
"layout (location = 0) in vec4 pos;\n"
+82 -115
View File
@@ -33,9 +33,10 @@
#include "thin3d/VulkanContext.h"
// We use a simple descriptor set for all rendering: 1 sampler, 1 texture, 1 UBO binding point.
// binding 0 - vertex data
// binding 1 - uniform data
// binding 2 - sampler
// binding 0 - uniform data
// binding 1 - sampler
//
// Vertex data lives in a separate namespace (location = 0, 1, etc)
#define VK_PROTOTYPES
#include "ext/vulkan/vulkan.h"
@@ -383,6 +384,10 @@ public:
void SetVector(const char *name, float *value, int n) override;
void SetMatrix4x4(const char *name, const float value[16]) override;
int GetUBOSize() const {
return uboSize_;
}
Thin3DVKShader *vshader;
Thin3DVKShader *fshader;
@@ -426,6 +431,7 @@ struct DescriptorSetKey {
if (texture_ < other.texture_) return true; else if (texture_ > other.texture_) return false;
if (vertexFormat_ < other.vertexFormat_) return true; else if (vertexFormat_ > other.vertexFormat_) return false;
if (sampler_ < other.sampler_) return true; else if (sampler_ > other.sampler_) return false;
if (frame < other.frame) return true; else if (frame > other.frame) return false;
return false;
}
};
@@ -507,7 +513,6 @@ private:
void DirtyDynamicState();
void BeginInitCommands();
void EndInitCommands();
VulkanContext *vulkan_;
@@ -527,17 +532,13 @@ private:
std::map<DescriptorSetKey, VkDescriptorSet> descSets_;
VkDescriptorPool descriptorPool_;
VkDescriptorSet descriptorSet_;
VkDescriptorSetLayout descriptorSetLayout_;
VkPipelineLayout pipelineLayout_;
VkPipelineCache pipelineCache_;
VkCommandPool cmdPool_;
VkInstance instance_;
VkPhysicalDevice physicalDevice_;
VkDevice device_;
VkQueue queue_;
VkRenderPass renderPass_;
int queueFamilyIndex_;
// State to apply at the next draw call if viewportDirty or scissorDirty are true.
@@ -557,20 +558,11 @@ private:
VkCommandBuffer initCmd_;
bool hasInitCommands_;
VkFence initFence_;
bool pendingInitFence_;
// TODO: Transpose this into a struct FrameObject[2].
// We write to one, while we wait for the draws from the other to complete.
// Then, at the end of the frame, they switch roles.
// cmdBuf_ for commands, pushBuffer_ for data. cmd_ will often refer to push_.
VkCommandBuffer cmdBuffer_[2];
VkCommandBuffer cmd_; // The current one
VkFence cmdFences_[2];
VkFence cmdFence_;
VulkanPushBuffer *pushBuffer_[2];
int frameNum_;
VulkanPushBuffer *push_;
@@ -674,7 +666,6 @@ private:
VulkanImage staging_;
VkImageView view_;
int32_t width_, height_, depth_;
int mipLevels_;
T3DImageFormat format_;
@@ -682,14 +673,22 @@ private:
};
Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan)
: viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan) {
: viewportDirty_(false), scissorDirty_(false), vulkan_(vulkan), frameNum_(0) {
device_ = vulkan->GetDevice();
queue_ = vulkan->GetGraphicsQueue();
queueFamilyIndex_ = vulkan->GetGraphicsQueueFamilyIndex();
noScissor_.offset.x = 0;
noScissor_.offset.y = 0;
noScissor_.extent.width = pixel_xres;
noScissor_.extent.height = pixel_yres;
scissor_ = noScissor_;
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();
@@ -703,18 +702,16 @@ Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan)
VkResult res = vkCreateCommandPool(device_, &p, nullptr, &cmdPool_);
assert(VK_SUCCESS == res);
VkDescriptorPoolSize dpTypes[3];
VkDescriptorPoolSize dpTypes[2];
dpTypes[0].descriptorCount = 200;
dpTypes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
dpTypes[1].descriptorCount = 1;
dpTypes[1].descriptorCount = 2;
dpTypes[1].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
dpTypes[2].descriptorCount = 1;
dpTypes[2].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
VkDescriptorPoolCreateInfo dp;
dp.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
dp.pNext = nullptr;
dp.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // We want to individually alloc and free descriptor sets. (do we?)
dp.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // We want to individually alloc and free descriptor sets. (do we? or one per "frame"?)
dp.maxSets = 200; // One set for every texture available... sigh
dp.pPoolSizes = dpTypes;
dp.poolSizeCount = ARRAY_SIZE(dpTypes);
@@ -723,31 +720,25 @@ Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan)
pushBuffer_[0] = new VulkanPushBuffer(device_, vulkan_, 1024 * 1024);
pushBuffer_[1] = new VulkanPushBuffer(device_, vulkan_, 1024 * 1024);
// binding 0 - vertex data
// binding 1 - uniform data
// binding 2 - sampler
// binding 3 - image
VkDescriptorSetLayoutBinding bindings[4];
// binding 0 - uniform data
// binding 1 - sampler
// binding 2 - image
VkDescriptorSetLayoutBinding bindings[2];
bindings[0].descriptorCount = 1;
bindings[0].pImmutableSamplers = nullptr;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[0].binding = 0;
bindings[1].descriptorCount = 1;
bindings[1].pImmutableSamplers = nullptr;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[1].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[1].binding = 1;
bindings[2].descriptorCount = 1;
bindings[2].pImmutableSamplers = nullptr;
bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[2].binding = 2;
VkDescriptorSetLayoutCreateInfo dsl;
dsl.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
dsl.pNext = nullptr;
dsl.bindingCount = 3;
dsl.bindingCount = 2;
dsl.pBindings = bindings;
res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &descriptorSetLayout_);
assert(VK_SUCCESS == res);
@@ -768,10 +759,6 @@ Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan)
cb.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cb.commandPool = cmdPool_;
cb.commandBufferCount = 1;
res = vkAllocateCommandBuffers(device_, &cb, &cmdBuffer_[0]);
assert(VK_SUCCESS == res);
res = vkAllocateCommandBuffers(device_, &cb, &cmdBuffer_[1]);
assert(VK_SUCCESS == res);
res = vkAllocateCommandBuffers(device_, &cb, &initCmd_);
assert(VK_SUCCESS == res);
hasInitCommands_ = false;
@@ -780,16 +767,7 @@ Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan)
f.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
f.pNext = nullptr;
f.flags = 0;
res = vkCreateFence(device_, &f, nullptr, &cmdFences_[0]);
assert(VK_SUCCESS == res);
f.flags = VK_FENCE_CREATE_SIGNALED_BIT;
res = vkCreateFence(device_, &f, nullptr, &cmdFences_[1]);
assert(VK_SUCCESS == res);
// Create as already signalled, so we can wait for it the first time.
res = vkCreateFence(device_, &f, nullptr, &initFence_);
assert(VK_SUCCESS == res);
pendingInitFence_ = false;
vkCreateFence(device_, &f, nullptr, &initFence_);
VkPipelineCacheCreateInfo pc;
pc.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
@@ -799,18 +777,12 @@ Thin3DVKContext::Thin3DVKContext(VulkanContext *vulkan)
pc.flags = 0;
res = vkCreatePipelineCache(device_, &pc, nullptr, &pipelineCache_);
assert(VK_SUCCESS == res);
push_ = pushBuffer_[0];
cmd_ = cmdBuffer_[0];
cmdFence_ = cmdFences_[0];
}
Thin3DVKContext::~Thin3DVKContext() {
for (auto x : pipelines_) {
vkDestroyPipeline(device_, x.second, nullptr);
}
vkFreeCommandBuffers(device_, cmdPool_, 2, cmdBuffer_);
vkFreeCommandBuffers(device_, cmdPool_, 1, &cmd_);
vkDestroyCommandPool(device_, cmdPool_, nullptr);
// This also destroys all descriptor sets.
vkDestroyDescriptorPool(device_, descriptorPool_, nullptr);
@@ -820,24 +792,28 @@ Thin3DVKContext::~Thin3DVKContext() {
}
void Thin3DVKContext::Begin(bool clear, uint32_t colorval, float depthVal, int stencilVal) {
VkClearValue clearVal[2];
VkClearValue clearVal[2] = {};
Uint8x4ToFloat4(colorval, clearVal[0].color.float32);
clearVal[0].color.float32[2] = 1.0f;
if (frameNum_ & 1)
clearVal[0].color.float32[2] = 1.0f;
clearVal[1].depthStencil.depth = depthVal;
clearVal[1].depthStencil.stencil = stencilVal;
vulkan_->BeginSurfaceRenderPass(clearVal);
// Make sure we don't stomp over the old command buffer.
vkWaitForFences(device_, 1, &cmdFence_, true, 0);
cmd_ = vulkan_->BeginSurfaceRenderPass(clearVal);
push_ = pushBuffer_[frameNum_ & 1];
// OK, we now know that nothing is reading from this frame's data pushbuffer,
// and that the command buffer can be safely reset and reused. So let's do that.
push_->Begin(device_);
scissorDirty_ = true;
viewportDirty_ = true;
}
void Thin3DVKContext::BeginInitCommands() {
assert(!hasInitCommands_);
// Before we can begin, we must be sure that the command buffer is no longer in use, as we only have a single one for init
// tasks (for now).
VkCommandBufferBeginInfo begin;
begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin.pNext = nullptr;
@@ -848,49 +824,34 @@ void Thin3DVKContext::BeginInitCommands() {
hasInitCommands_ = true;
}
void Thin3DVKContext::EndInitCommands() {
VkResult res = vkEndCommandBuffer(initCmd_);
assert(VK_SUCCESS == res);
}
void Thin3DVKContext::End() {
// Stop collecting data in the frame data buffer.
push_->End(device_);
vkCmdEndRenderPass(cmd_);
VkResult endRes = vkEndCommandBuffer(cmd_);
// IF something needs to be uploaded etc, sneak it in before we actually run the main command buffer.
if (hasInitCommands_) {
assert(!pendingInitFence_);
EndInitCommands();
VkResult res = vkEndCommandBuffer(initCmd_);
assert(VK_SUCCESS == res);
// Run the texture uploads etc _before_ we execute the ordinary command buffer
pendingInitFence_ = true;
VkSubmitInfo submit = {};
submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit.pCommandBuffers = &initCmd_;
submit.commandBufferCount = 1;
VkResult res = vkQueueSubmit(queue_, 1, &submit, initFence_);
res = vkQueueSubmit(queue_, 1, &submit, initFence_);
assert(VK_SUCCESS == res);
// Before we can begin, we must be sure that the command buffer is no longer in use, as we only have a single one for init
// tasks (for now).
vulkan_->WaitAndResetFence(initFence_);
hasInitCommands_ = false;
// Init cmd buffer is again available for writing.
}
if (VK_SUCCESS != endRes) {
ELOG("vkEndCommandBuffer failed");
vkResetCommandBuffer(cmd_, 0);
} else {
VkSubmitInfo submit = {};
submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit.pCommandBuffers = &cmd_;
submit.commandBufferCount = 1;
VkResult res = vkQueueSubmit(queue_, 1, &submit, cmdFence_);
assert(VK_SUCCESS == res);
}
vulkan_->EndSurfaceRenderPass();
frameNum_++;
push_ = pushBuffer_[frameNum_ & 1];
cmd_ = cmdBuffer_[frameNum_ & 1];
cmdFence_ = cmdFences_[frameNum_ & 1];
cmd_ = nullptr; // will be set on the next begin
push_ = nullptr;
DirtyDynamicState();
}
@@ -917,14 +878,13 @@ VkDescriptorSet Thin3DVKContext::GetOrCreateDescriptorSet() {
VkResult res = vkAllocateDescriptorSets(device_, &alloc, &descSet);
assert(VK_SUCCESS == res);
// bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
// bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
// bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
// bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
// bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
VkDescriptorBufferInfo bufferDesc;
bufferDesc.buffer = push_->GetVkBuffer();
bufferDesc.offset = 0;
bufferDesc.range = 16 * 4;
bufferDesc.range = curShaderSet_->GetUBOSize();
VkDescriptorImageInfo imageDesc;
imageDesc.imageView = boundTextures_[0]->GetImageView();
@@ -937,7 +897,7 @@ VkDescriptorSet Thin3DVKContext::GetOrCreateDescriptorSet() {
writes[0].pNext = nullptr;
writes[0].dstSet = descSet;
writes[0].dstArrayElement = 0;
writes[0].dstBinding = 1;
writes[0].dstBinding = 0;
writes[0].pBufferInfo = &bufferDesc;
writes[0].descriptorCount = 1;
writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
@@ -946,7 +906,7 @@ VkDescriptorSet Thin3DVKContext::GetOrCreateDescriptorSet() {
writes[1].pNext = nullptr;
writes[1].dstSet = descSet;
writes[1].dstArrayElement = 0;
writes[1].dstBinding = 2;
writes[1].dstBinding = 1;
writes[1].pImageInfo = &imageDesc;
writes[1].descriptorCount = 1;
writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
@@ -1062,7 +1022,7 @@ VkPipeline Thin3DVKContext::GetOrCreatePipeline() {
info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically.
info.layout = pipelineLayout_;
info.subpass = 0;
info.renderPass = renderPass_;
info.renderPass = vulkan_->GetSurfaceRenderPass();
// OK, need to create a new pipeline.
VkPipeline pipeline;
@@ -1136,8 +1096,8 @@ void Thin3DVKTexture::SetImageData(int x, int y, int z, int width, int height, i
// So we need to do a staging copy. We upload the data to the staging buffer immediately, then we actually do the final copy once it's used the first time
// as we need a command buffer and the architecture of Thin3D doesn't really work the way we want..
if (!image_.IsValid()) {
staging_.Create2D(vulkan_, vulkanFormat, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, width, height);
image_.Create2D(vulkan_, vulkanFormat, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), width, height);
staging_.Create2D(vulkan_, vulkanFormat, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, width, height);
image_.Create2D(vulkan_, vulkanFormat, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TILING_OPTIMAL, (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), width, height);
}
VkImageViewCreateInfo iv;
@@ -1162,6 +1122,8 @@ void Thin3DVKTexture::SetImageData(int x, int y, int z, int width, int height, i
// TODO: Support setting only parts of the image efficiently.
staging_.SetImageData2D(vulkan_->GetDevice(), data, width, height, stride);
state_ = TextureState::STAGED;
width_ = width;
height_ = height;
}
void Thin3DVKTexture::Finalize(int zim_flags) {
@@ -1173,20 +1135,23 @@ bool Thin3DVKTexture::NeedsUpload() {
}
void Thin3DVKTexture::Upload(VkCommandBuffer cmd) {
if (state_ == TextureState::STAGED) {
// Before we can texture, we need to Copy and ChangeLayout.
VkImageCopy copy_region;
copy_region.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
copy_region.srcOffset = { 0, 0, 0 };
copy_region.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
copy_region.dstOffset = { 0, 0, 0 };
copy_region.extent = { (uint32_t)width_, (uint32_t)height_, 1 };
vkCmdCopyImage(cmd, staging_.GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_.GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
image_.ChangeLayout(cmd, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// From this point on, the image can be used for texturing.
// Even before this function call (but after SetImageData), the image object can be referenced in a descriptor set.
state_ = TextureState::INITIALIZED;
if (state_ != TextureState::STAGED) {
return;
}
// Before we can texture, we need to Copy and ChangeLayout.
VkImageCopy copy_region;
copy_region.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
copy_region.srcOffset = { 0, 0, 0 };
copy_region.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
copy_region.dstOffset = { 0, 0, 0 };
copy_region.extent = { (uint32_t)width_, (uint32_t)height_, 1 };
vkCmdCopyImage(cmd, staging_.GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image_.GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
image_.ChangeLayout(cmd, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// From this point on, the image can be used for texturing.
// Even before this function call (but after SetImageData), the image object can be referenced in a descriptor set. Better make sure that the image is uploaded
// before it's actually used though...
state_ = TextureState::INITIALIZED;
}
static bool isPowerOf2(int n) {
@@ -1317,6 +1282,7 @@ void Thin3DVKContext::SetRenderState(T3DRenderState rs, uint32_t value) {
}
void Thin3DVKContext::Draw(T3DPrimitive prim, Thin3DShaderSet *shaderSet, Thin3DVertexFormat *format, Thin3DBuffer *vdata, int vertexCount, int offset) {
return;
ApplyDynamicState();
curPrim_ = primToVK[prim];
@@ -1338,6 +1304,7 @@ void Thin3DVKContext::Draw(T3DPrimitive prim, Thin3DShaderSet *shaderSet, Thin3D
}
void Thin3DVKContext::DrawIndexed(T3DPrimitive prim, Thin3DShaderSet *shaderSet, Thin3DVertexFormat *format, Thin3DBuffer *vdata, Thin3DBuffer *idata, int vertexCount, int offset) {
return;
ApplyDynamicState();
curPrim_ = primToVK[prim];
+12 -7
View File
@@ -40,12 +40,12 @@
#include "thin3d/vulkan_utils.h"
void VulkanImage::Create2D(VulkanContext *vulkan, VkFormat format, VkFlags required_props, VkImageUsageFlags usage, int width, int height) {
void VulkanImage::Create2D(VulkanContext *vulkan, VkFormat format, VkFlags required_props, VkImageTiling tiling, VkImageUsageFlags usage, int width, int height) {
VkDevice device = vulkan->GetDevice();
width_ = width;
height_ = height;
VkImageCreateInfo i;
VkImageCreateInfo i = {};
i.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
i.pNext = NULL;
i.imageType = VK_IMAGE_TYPE_2D;
@@ -54,13 +54,13 @@ void VulkanImage::Create2D(VulkanContext *vulkan, VkFormat format, VkFlags requi
i.mipLevels = 1;
i.arrayLayers = 1;
i.samples = VK_SAMPLE_COUNT_1_BIT;
i.tiling = VK_IMAGE_TILING_LINEAR;
i.tiling = tiling;
i.usage = usage;
i.flags = 0;
i.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
i.queueFamilyIndexCount = 0;
i.pQueueFamilyIndices = nullptr;
i.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
i.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkMemoryRequirements mem_reqs;
@@ -81,6 +81,7 @@ void VulkanImage::Create2D(VulkanContext *vulkan, VkFormat format, VkFlags requi
assert(!err);
err = vkBindImageMemory(device, image_, memory_, 0); // at offset 0.
assert(!err);
}
void VulkanImage::SetImageData2D(VkDevice device, const uint8_t *data, int width, int height, int pitch) {
@@ -121,14 +122,18 @@ void VulkanImage::ChangeLayout(VkCommandBuffer cmd, VkImageAspectFlags aspectMas
image_memory_barrier.subresourceRange.baseMipLevel = 0;
image_memory_barrier.subresourceRange.levelCount = 1;
if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
/* Make sure anything that was copying from this image has completed */
// Make sure anything that was copying from this image has completed
image_memory_barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
}
if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
/* Make sure any Copy or CPU writes to image are flushed */
image_memory_barrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
// Make sure any Copy or CPU writes to image are flushed
image_memory_barrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
}
VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+2 -4
View File
@@ -23,7 +23,7 @@
#include "ext/vulkan/vulkan.h"
#include "VulkanContext.h"
class VulkanContext;
// Utility class to handle images without going insane.
// Allocates its own memory.
class VulkanImage {
@@ -32,7 +32,7 @@ public:
bool IsValid() const { return image_ != nullptr; }
// This can be done completely unsynchronized.
void Create2D(VulkanContext *vulkan, VkFormat format, VkFlags required_props, VkImageUsageFlags usage, int width, int height);
void Create2D(VulkanContext *vulkan, VkFormat format, VkFlags required_props, VkImageTiling tiling, VkImageUsageFlags usage, int width, int height);
// This can only be used if you pass in VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in required_props in Create2D.
void SetImageData2D(VkDevice device, const uint8_t *data, int width, int height, int pitch);
@@ -54,6 +54,4 @@ class Thin3DPipelineCache {
};
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<uint32_t> &spirv);
bool CreateShaderModule(VkDevice device, const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule);