mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
vulkan: implement graphics readback
This commit is contained in:
@@ -38,7 +38,12 @@ bool Buffer::loadVolatile() {
|
||||
VkBufferCreateInfo bufferInfo{};
|
||||
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
bufferInfo.size = getSize();
|
||||
bufferInfo.usage = getVulkanUsageFlags(usageFlags);
|
||||
if (dataUsage == BUFFERDATAUSAGE_READBACK) {
|
||||
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
else {
|
||||
bufferInfo.usage = getVulkanUsageFlags(usageFlags);
|
||||
}
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
@@ -88,7 +93,7 @@ void Buffer::unmap(size_t usedoffset, size_t usedsize) {
|
||||
void Buffer::copyTo(love::graphics::Buffer* dest, size_t sourceoffset, size_t destoffset, size_t size) {
|
||||
Graphics* vgfx = (Graphics*)gfx;
|
||||
|
||||
auto commandBuffer = vgfx->getDataTransferCommandBuffer();
|
||||
auto commandBuffer = vgfx->getReadbackCommandBuffer();
|
||||
|
||||
VkBufferCopy bufferCopy{};
|
||||
bufferCopy.srcOffset = sourceoffset;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Graphics.h"
|
||||
#include "Buffer.h"
|
||||
#include "GraphicsReadback.h"
|
||||
#include "SDL_vulkan.h"
|
||||
#include "window/Window.h"
|
||||
#include "common/Exception.h"
|
||||
@@ -147,50 +148,81 @@ void Graphics::clear(const std::vector<OptionalColorD>& colors, OptionalInt sten
|
||||
vkCmdClearAttachments(commandBuffers[currentFrame], static_cast<uint32_t>(attachments.size()), attachments.data(), 1, &rect);
|
||||
}
|
||||
|
||||
void Graphics::present(void* screenshotCallbackdata) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Graphics::submitGpuCommands(bool present) {
|
||||
flushBatchedDraws();
|
||||
|
||||
endRecordingGraphicsCommands();
|
||||
endRecordingGraphicsCommands(present);
|
||||
|
||||
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
|
||||
vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX);
|
||||
}
|
||||
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
|
||||
|
||||
// all data transfers should happen before any draw calls.
|
||||
std::vector<VkCommandBuffer> submitCommandbuffers = { dataTransferCommandBuffers.at(currentFrame), commandBuffers.at(currentFrame) };
|
||||
std::vector<VkCommandBuffer> submitCommandbuffers = {
|
||||
dataTransferCommandBuffers.at(currentFrame),
|
||||
commandBuffers.at(currentFrame),
|
||||
readbackCommandBuffers.at(currentFrame)};
|
||||
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
|
||||
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) };
|
||||
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pWaitSemaphores = waitSemaphores;
|
||||
submitInfo.pWaitDstStageMask = waitStages;
|
||||
|
||||
if (imageRequested) {
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pWaitSemaphores = waitSemaphores;
|
||||
submitInfo.pWaitDstStageMask = waitStages;
|
||||
imageRequested = false;
|
||||
}
|
||||
|
||||
submitInfo.commandBufferCount = static_cast<uint32_t>(submitCommandbuffers.size());
|
||||
submitInfo.pCommandBuffers = submitCommandbuffers.data();
|
||||
|
||||
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||
|
||||
vkResetFences(device, 1, &inFlightFences[currentFrame]);
|
||||
VkFence fence = VK_NULL_HANDLE;
|
||||
|
||||
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences.at(currentFrame)) != VK_SUCCESS) {
|
||||
if (present) {
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||
|
||||
vkResetFences(device, 1, &inFlightFences[currentFrame]);
|
||||
fence = inFlightFences[currentFrame];
|
||||
}
|
||||
|
||||
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to submit draw command buffer");
|
||||
}
|
||||
|
||||
if (!present) {
|
||||
vkQueueWaitIdle(graphicsQueue);
|
||||
|
||||
for (auto& callbacks : readbackCallbacks) {
|
||||
for (const auto& callback : callbacks) {
|
||||
callback();
|
||||
}
|
||||
callbacks.clear();
|
||||
}
|
||||
|
||||
startRecordingGraphicsCommands(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::present(void* screenshotCallbackdata) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
submitGpuCommands(true);
|
||||
|
||||
VkPresentInfoKHR presentInfo{};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
|
||||
VkSemaphore waitSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
|
||||
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
presentInfo.pWaitSemaphores = signalSemaphores;
|
||||
presentInfo.pWaitSemaphores = waitSemaphores;
|
||||
|
||||
VkSwapchainKHR swapChains[] = { swapChain };
|
||||
presentInfo.swapchainCount = 1;
|
||||
@@ -210,8 +242,9 @@ void Graphics::present(void* screenshotCallbackdata) {
|
||||
|
||||
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
|
||||
beginFrame();
|
||||
|
||||
updatedBatchedDrawBuffers();
|
||||
startRecordingGraphicsCommands();
|
||||
}
|
||||
|
||||
void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) {
|
||||
@@ -229,6 +262,9 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
|
||||
cleanUpFunctions.clear();
|
||||
cleanUpFunctions.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
readbackCallbacks.clear();
|
||||
readbackCallbacks.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
createVulkanInstance();
|
||||
createSurface();
|
||||
pickPhysicalDevice();
|
||||
@@ -244,13 +280,6 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
|
||||
createDefaultFramebuffers();
|
||||
createCommandPool();
|
||||
createCommandBuffers();
|
||||
startRecordingGraphicsCommands();
|
||||
createQuadIndexBuffer();
|
||||
createDefaultTexture();
|
||||
createDefaultShaders();
|
||||
currentFrame = 0;
|
||||
|
||||
created = true;
|
||||
|
||||
float whiteColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
|
||||
@@ -271,18 +300,27 @@ bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int
|
||||
batchedDrawBuffers[i].constantColorBuffer->unmap(sizeof(whiteColor));
|
||||
batchedDrawBuffers[i].constantColorBuffer->markUsed(sizeof(whiteColor));
|
||||
}
|
||||
|
||||
updatedBatchedDrawBuffers();
|
||||
|
||||
beginFrame();
|
||||
|
||||
createDefaultTexture();
|
||||
createDefaultShaders();
|
||||
Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT];
|
||||
createQuadIndexBuffer();
|
||||
|
||||
restoreState(states.back());
|
||||
|
||||
setViewportSize(width, height, pixelwidth, pixelheight);
|
||||
currentViewportWidth = 0.0f;
|
||||
currentViewportHeight = 0.0f;
|
||||
currentViewportWidth = 1.0f;
|
||||
currentViewportHeight = 1.0f;
|
||||
|
||||
Vulkan::resetShaderSwitches();
|
||||
|
||||
currentFrame = 0;
|
||||
|
||||
created = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -301,10 +339,10 @@ void Graphics::initCapabilities() {
|
||||
capabilities.features[FEATURE_INSTANCING] = true;
|
||||
capabilities.features[FEATURE_TEXEL_BUFFER] = false;
|
||||
capabilities.features[FEATURE_INDEX_BUFFER_32BIT] = true;
|
||||
capabilities.features[FEATURE_COPY_BUFFER] = false;
|
||||
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = false;
|
||||
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = false;
|
||||
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = false;
|
||||
capabilities.features[FEATURE_COPY_BUFFER] = true;
|
||||
capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = true;
|
||||
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = true;
|
||||
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = true;
|
||||
static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
|
||||
|
||||
VkPhysicalDeviceProperties properties;
|
||||
@@ -584,6 +622,22 @@ Renderer Graphics::getRenderer() const {
|
||||
return RENDERER_VULKAN;
|
||||
}
|
||||
|
||||
graphics::GraphicsReadback* Graphics::newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) {
|
||||
return new GraphicsReadback(this, method, buffer, offset, size, dest, destoffset);
|
||||
}
|
||||
|
||||
graphics::GraphicsReadback* Graphics::newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) {
|
||||
return new GraphicsReadback(this, method, texture, slice, mipmap, rect, dest, destx, desty);
|
||||
}
|
||||
|
||||
graphics::ShaderStage* Graphics::newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) {
|
||||
return new ShaderStage(this, stage, source, gles, cachekey);
|
||||
}
|
||||
|
||||
graphics::Shader* Graphics::newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) {
|
||||
return new Shader(stages);
|
||||
}
|
||||
|
||||
graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
|
||||
return new StreamBuffer(this, type, size);
|
||||
}
|
||||
@@ -645,7 +699,7 @@ void Graphics::initDynamicState() {
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::startRecordingGraphicsCommands() {
|
||||
void Graphics::beginFrame() {
|
||||
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
|
||||
|
||||
while (true) {
|
||||
@@ -661,11 +715,24 @@ void Graphics::startRecordingGraphicsCommands() {
|
||||
break;
|
||||
}
|
||||
|
||||
imageRequested = true;
|
||||
|
||||
for (auto& readbackCallback : readbackCallbacks.at(currentFrame)) {
|
||||
readbackCallback();
|
||||
}
|
||||
readbackCallbacks.at(currentFrame).clear();
|
||||
|
||||
for (auto& cleanUpFn : cleanUpFunctions.at(currentFrame)) {
|
||||
cleanUpFn();
|
||||
}
|
||||
cleanUpFunctions.at(currentFrame).clear();
|
||||
|
||||
startRecordingGraphicsCommands(true);
|
||||
|
||||
Vulkan::resetShaderSwitches();
|
||||
}
|
||||
|
||||
void Graphics::startRecordingGraphicsCommands(bool newFrame) {
|
||||
VkCommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
beginInfo.flags = 0;
|
||||
@@ -677,20 +744,25 @@ void Graphics::startRecordingGraphicsCommands() {
|
||||
if (vkBeginCommandBuffer(dataTransferCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to begin recording data transfer command buffer");
|
||||
}
|
||||
if (vkBeginCommandBuffer(readbackCommandBuffers.at(currentFrame), &beginInfo) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to begin recording readback command buffer");
|
||||
}
|
||||
|
||||
initDynamicState();
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
if (newFrame) {
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
startDefaultRenderPass();
|
||||
|
||||
Vulkan::resetShaderSwitches();
|
||||
}
|
||||
|
||||
void Graphics::endRecordingGraphicsCommands() {
|
||||
void Graphics::endRecordingGraphicsCommands(bool present) {
|
||||
endRenderPass();
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
if (present) {
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffers.at(currentFrame), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
}
|
||||
|
||||
if (vkEndCommandBuffer(commandBuffers.at(currentFrame)) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to record command buffer");
|
||||
@@ -698,6 +770,9 @@ void Graphics::endRecordingGraphicsCommands() {
|
||||
if (vkEndCommandBuffer(dataTransferCommandBuffers.at(currentFrame)) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to record data transfer command buffer");
|
||||
}
|
||||
if (vkEndCommandBuffer(readbackCommandBuffers.at(currentFrame)) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to record read back command buffer");
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::updatedBatchedDrawBuffers() {
|
||||
@@ -725,10 +800,18 @@ VkCommandBuffer Graphics::getDataTransferCommandBuffer() {
|
||||
return dataTransferCommandBuffers.at(currentFrame);
|
||||
}
|
||||
|
||||
VkCommandBuffer Graphics::getReadbackCommandBuffer() {
|
||||
return readbackCommandBuffers.at(currentFrame);
|
||||
}
|
||||
|
||||
void Graphics::queueCleanUp(std::function<void()> cleanUp) {
|
||||
cleanUpFunctions.at(currentFrame).push_back(std::move(cleanUp));
|
||||
}
|
||||
|
||||
void Graphics::addReadbackCallback(const std::function<void()>& callback) {
|
||||
readbackCallbacks.at(currentFrame).push_back(std::move(callback));
|
||||
}
|
||||
|
||||
graphics::Shader::BuiltinUniformData Graphics::getCurrentBuiltinUniformData() {
|
||||
love::graphics::Shader::BuiltinUniformData data;
|
||||
|
||||
@@ -1036,7 +1119,6 @@ void Graphics::createLogicalDevice() {
|
||||
createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size());
|
||||
createInfo.ppEnabledExtensionNames = enabledExtensions.data();
|
||||
|
||||
// can this be removed?
|
||||
if (enableValidationLayers) {
|
||||
createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
|
||||
createInfo.ppEnabledLayerNames = validationLayers.data();
|
||||
@@ -1520,14 +1602,24 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration configuration) {
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
VkSubpassDependency readbackDependency{};
|
||||
readbackDependency.srcSubpass = 0;
|
||||
readbackDependency.dstSubpass = VK_SUBPASS_EXTERNAL;
|
||||
readbackDependency.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
readbackDependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
readbackDependency.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
readbackDependency.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
|
||||
std::array<VkSubpassDependency, 2> dependencies = { dependency, readbackDependency };
|
||||
|
||||
VkRenderPassCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
createInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
createInfo.pAttachments = attachments.data();
|
||||
createInfo.subpassCount = 1;
|
||||
createInfo.pSubpasses = &subPass;
|
||||
createInfo.dependencyCount = 1;
|
||||
createInfo.pDependencies = &dependency;
|
||||
createInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
|
||||
createInfo.pDependencies = dependencies.data();
|
||||
|
||||
VkRenderPass renderPass;
|
||||
if (vkCreateRenderPass(device, &createInfo, nullptr, &renderPass) != VK_SUCCESS) {
|
||||
@@ -1681,6 +1773,16 @@ void Graphics::startDefaultRenderPass() {
|
||||
currentViewportHeight = (float)swapChainExtent.height;
|
||||
currentMsaaSamples = msaaSamples;
|
||||
currentNumColorAttachments = 1;
|
||||
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = currentViewportWidth;
|
||||
viewport.height = currentViewportHeight;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
vkCmdSetViewport(commandBuffers.at(currentFrame), 0, 1, &viewport);
|
||||
}
|
||||
|
||||
void Graphics::startRenderPass(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) {
|
||||
@@ -2151,12 +2253,13 @@ void Graphics::createCommandPool() {
|
||||
void Graphics::createCommandBuffers() {
|
||||
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
dataTransferCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
readbackCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
VkCommandBufferAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
allocInfo.commandPool = commandPool;
|
||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
allocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT;
|
||||
allocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to allocate command buffers");
|
||||
@@ -2166,11 +2269,21 @@ void Graphics::createCommandBuffers() {
|
||||
dataTransferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
dataTransferAllocInfo.commandPool = commandPool;
|
||||
dataTransferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
dataTransferAllocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT;
|
||||
dataTransferAllocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
if (vkAllocateCommandBuffers(device, &dataTransferAllocInfo, dataTransferCommandBuffers.data()) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to allocate data transfer command buffers");
|
||||
}
|
||||
|
||||
VkCommandBufferAllocateInfo readbackAllocInfo{};
|
||||
readbackAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
readbackAllocInfo.commandPool = commandPool;
|
||||
readbackAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
readbackAllocInfo.commandBufferCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
if (vkAllocateCommandBuffers(device, &readbackAllocInfo, readbackCommandBuffers.data()) != VK_SUCCESS) {
|
||||
throw love::Exception("failed to allocate readback command buffers");
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::createSyncObjects() {
|
||||
|
||||
@@ -219,11 +219,16 @@ public:
|
||||
void draw(const DrawIndexedCommand& cmd) override;
|
||||
void drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) override;
|
||||
|
||||
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override { return nullptr; };
|
||||
GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) override { return nullptr; }
|
||||
graphics::GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset) override;
|
||||
graphics::GraphicsReadback* newReadbackInternal(ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty) override;
|
||||
|
||||
VkCommandBuffer getDataTransferCommandBuffer();
|
||||
VkCommandBuffer getReadbackCommandBuffer();
|
||||
|
||||
void queueCleanUp(std::function<void()> cleanUp);
|
||||
void addReadbackCallback(const std::function<void()> &callback);
|
||||
|
||||
void submitGpuCommands(bool present);
|
||||
|
||||
uint32_t getNumImagesInFlight() const;
|
||||
const VkDeviceSize getMinUniformBufferOffsetAlignment() const;
|
||||
@@ -231,12 +236,8 @@ public:
|
||||
VkSampler getCachedSampler(const SamplerState&);
|
||||
|
||||
protected:
|
||||
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override {
|
||||
return new ShaderStage(this, stage, source, gles, cachekey);
|
||||
}
|
||||
graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override {
|
||||
return new Shader(stages);
|
||||
}
|
||||
graphics::ShaderStage* newShaderStageInternal(ShaderStageType stage, const std::string& cachekey, const std::string& source, bool gles) override;
|
||||
graphics::Shader* newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM]) override;
|
||||
graphics::StreamBuffer* newStreamBuffer(BufferUsage type, size_t size) override;
|
||||
bool dispatch(int x, int y, int z) override { return false; }
|
||||
void initCapabilities() override;
|
||||
@@ -280,8 +281,9 @@ private:
|
||||
void cleanupSwapChain();
|
||||
void recreateSwapChain();
|
||||
void initDynamicState();
|
||||
void startRecordingGraphicsCommands();
|
||||
void endRecordingGraphicsCommands();
|
||||
void beginFrame();
|
||||
void startRecordingGraphicsCommands(bool newFrame);
|
||||
void endRecordingGraphicsCommands(bool present);
|
||||
void ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration);
|
||||
graphics::Shader::BuiltinUniformData getCurrentBuiltinUniformData();
|
||||
void updatedBatchedDrawBuffers();
|
||||
@@ -329,13 +331,15 @@ private:
|
||||
std::unordered_map<GraphicsPipelineConfiguration, VkPipeline, GraphicsPipelineConfigurationHasher> graphicsPipelines;
|
||||
std::unordered_map<SamplerState, VkSampler, SamplerStateHasher> samplers;
|
||||
VkCommandPool commandPool = VK_NULL_HANDLE;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
std::vector<VkCommandBuffer> dataTransferCommandBuffers;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
std::vector<VkCommandBuffer> readbackCommandBuffers;
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||
std::vector<VkFence> inFlightFences;
|
||||
std::vector<VkFence> imagesInFlight;
|
||||
VkDeviceSize minUniformBufferOffsetAlignment = 0;
|
||||
bool imageRequested = false;
|
||||
size_t currentFrame = 0;
|
||||
uint32_t imageIndex = 0;
|
||||
bool framebufferResized = false;
|
||||
@@ -347,6 +351,7 @@ private:
|
||||
// functions that need to be called to cleanup objects that were needed for rendering a frame.
|
||||
// just like batchedDrawBuffers we need a vector for each frame in flight.
|
||||
std::vector<std::vector<std::function<void()>>> cleanUpFunctions;
|
||||
std::vector<std::vector<std::function<void()>>> readbackCallbacks;
|
||||
|
||||
// render pass variables.
|
||||
std::optional<std::function<void()>> postRenderPass;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "GraphicsReadback.h"
|
||||
#include "Buffer.h"
|
||||
#include "Texture.h"
|
||||
#include "Graphics.h"
|
||||
#include "data/ByteData.h"
|
||||
|
||||
namespace love {
|
||||
namespace graphics {
|
||||
namespace vulkan {
|
||||
|
||||
GraphicsReadback::GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset)
|
||||
: graphics::GraphicsReadback(gfx, method, buffer, offset, size, dest, destoffset) {
|
||||
vgfx = dynamic_cast<Graphics*>(gfx);
|
||||
|
||||
// Immediate readback of readback-type buffers doesn't need a staging buffer.
|
||||
if (method != READBACK_IMMEDIATE || buffer->getDataUsage() != BUFFERDATAUSAGE_READBACK) {
|
||||
stagingBuffer = gfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
|
||||
gfx->copyBuffer(buffer, stagingBuffer, offset, 0, size);
|
||||
}
|
||||
|
||||
if (method == READBACK_IMMEDIATE) {
|
||||
vgfx->submitGpuCommands(false);
|
||||
if (stagingBuffer.get()) {
|
||||
status = readbackBuffer(stagingBuffer, 0, size);
|
||||
gfx->releaseTemporaryBuffer(stagingBuffer);
|
||||
}
|
||||
else
|
||||
status = readbackBuffer(buffer, offset, size);
|
||||
}
|
||||
else
|
||||
vgfx->addReadbackCallback([&]() {
|
||||
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
|
||||
|
||||
vgfx->releaseTemporaryBuffer(stagingBuffer);
|
||||
stagingBuffer.set(nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
GraphicsReadback::GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty)
|
||||
: graphics::GraphicsReadback(gfx, method, texture, slice, mipmap, rect, dest, destx, desty) {
|
||||
vgfx = dynamic_cast<Graphics*>(gfx);
|
||||
|
||||
size_t size = getPixelFormatSliceSize(textureFormat, rect.w, rect.h);
|
||||
|
||||
stagingBuffer = vgfx->getTemporaryBuffer(size, DATAFORMAT_FLOAT, 0, BUFFERDATAUSAGE_READBACK);
|
||||
|
||||
vgfx->copyTextureToBuffer(texture, stagingBuffer, slice, mipmap, rect, 0, 0);
|
||||
|
||||
vgfx->addReadbackCallback([&]() {
|
||||
status = readbackBuffer(stagingBuffer, 0, stagingBuffer->getSize());
|
||||
|
||||
vgfx->releaseTemporaryBuffer(stagingBuffer);
|
||||
stagingBuffer.set(nullptr);
|
||||
});
|
||||
|
||||
if (method == READBACK_IMMEDIATE)
|
||||
vgfx->submitGpuCommands(false);
|
||||
}
|
||||
|
||||
GraphicsReadback::~GraphicsReadback() {
|
||||
}
|
||||
|
||||
void GraphicsReadback::wait() {
|
||||
if (status == STATUS_WAITING)
|
||||
vgfx->submitGpuCommands(false);
|
||||
}
|
||||
|
||||
void GraphicsReadback::update() {
|
||||
}
|
||||
|
||||
} // vulkan
|
||||
} // graphics
|
||||
} // love
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef LOVE_GRAPHICS_VULKAN_GRAPHICS_READBACK_H
|
||||
#define LOVE_GRAPHICS_VULKAN_GRAPHICS_READBACK_H
|
||||
|
||||
#include "graphics/GraphicsReadback.h"
|
||||
|
||||
namespace love {
|
||||
namespace graphics {
|
||||
namespace vulkan {
|
||||
|
||||
class Graphics;
|
||||
|
||||
class GraphicsReadback : public graphics::GraphicsReadback {
|
||||
public:
|
||||
GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Buffer* buffer, size_t offset, size_t size, data::ByteData* dest, size_t destoffset);
|
||||
GraphicsReadback(love::graphics::Graphics* gfx, ReadbackMethod method, love::graphics::Texture* texture, int slice, int mipmap, const Rect& rect, image::ImageData* dest, int destx, int desty);
|
||||
virtual ~GraphicsReadback();
|
||||
|
||||
void wait() override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
|
||||
Graphics* vgfx;
|
||||
StrongRef<love::graphics::Buffer> stagingBuffer;
|
||||
};
|
||||
|
||||
} // vulkan
|
||||
} // graphics
|
||||
} // love
|
||||
|
||||
#endif
|
||||
@@ -391,7 +391,7 @@ void Texture::copyFromBuffer(graphics::Buffer* source, size_t sourceoffset, int
|
||||
}
|
||||
|
||||
void Texture::copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const Rect& rect, size_t destoffset, int destwidth, size_t size) {
|
||||
auto commandBuffer = vgfx->getDataTransferCommandBuffer();
|
||||
auto commandBuffer = vgfx->getReadbackCommandBuffer();
|
||||
|
||||
VkImageSubresourceLayers layers{};
|
||||
layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
@@ -402,10 +402,11 @@ void Texture::copyToBuffer(graphics::Buffer* dest, int slice, int mipmap, const
|
||||
VkBufferImageCopy region{};
|
||||
region.bufferOffset = destoffset;
|
||||
region.bufferRowLength = destwidth;
|
||||
region.bufferImageHeight = 1;
|
||||
region.bufferImageHeight = 0;
|
||||
region.imageSubresource = layers;
|
||||
region.imageExtent.width = static_cast<uint32_t>(rect.w);
|
||||
region.imageExtent.height = static_cast<uint32_t>(rect.h);
|
||||
region.imageExtent.depth = 1;
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user