vulkan: implement setRenderTargetsInternal

basic usage of canvases now works.
Colors probably aren't correct yet.
This commit is contained in:
niki
2022-07-15 01:44:07 +02:00
parent 0ae81091d1
commit 95952c691d
4 changed files with 135 additions and 48 deletions
+101 -41
View File
@@ -200,6 +200,11 @@ namespace love {
Shader::current = Shader::standardShaders[graphics::Shader::StandardShader::STANDARD_DEFAULT];
currentPolygonMode = VK_POLYGON_MODE_FILL;
restoreState(states.back());
setViewportSize(width, height, pixelwidth, pixelheight);
renderTargetTexture = nullptr;
currentViewportWidth = 0.0f;
currentViewportHeight = 0.0f;
return true;
}
@@ -317,6 +322,24 @@ namespace love {
vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(cmd.indexCount), static_cast<uint32_t>(cmd.instanceCount), 0, 0, 0);
}
void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK);
vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16));
int baseVertex = start * 4;
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) {
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(quadcount * 6), 1, 0, baseVertex, 0);
baseVertex += quadcount * 4;
}
}
void Graphics::setColor(Colorf c) {
c.r = std::min(std::max(c.r, 0.0f), 1.0f);
c.g = std::min(std::max(c.g, 0.0f), 1.0f);
@@ -363,24 +386,6 @@ namespace love {
return RENDERER_VULKAN;
}
void Graphics::drawQuads(int start, int count, const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture) {
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
prepareDraw(attributes, buffers, texture, PRIMITIVE_TRIANGLES, CULL_BACK);
vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)quadIndexBuffer->getHandle(), 0, getVulkanIndexBufferType(INDEX_UINT16));
int baseVertex = start * 4;
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) {
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(quadcount * 6), 1, 0, baseVertex, 0);
baseVertex += quadcount * 4;
}
}
graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
return new StreamBuffer(this, type, size);
}
@@ -391,6 +396,14 @@ namespace love {
}
void Graphics::setRenderTargetsInternal(const RenderTargets& rts, int pixelw, int pixelh, bool hasSRGBtexture) {
endRenderPass();
if (rts.colors.size() == 0) {
startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height);
} else {
auto firstRenderTarget = rts.getFirstTarget();
startRenderPass(static_cast<Texture*>(firstRenderTarget.texture), pixelw, pixelh);
}
}
// END IMPLEMENTATION OVERRIDDEN FUNCTIONS
@@ -420,34 +433,17 @@ namespace love {
throw love::Exception("failed to begin recording command buffer");
}
VkRenderingAttachmentInfo colorAttachmentInfo{};
colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
colorAttachmentInfo.imageView = swapChainImageViews[imageIndex];
colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachmentInfo.clearValue = clearColor;
VkRenderingInfo renderingInfo{};
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
renderingInfo.renderArea.extent = swapChainExtent;
renderingInfo.layerCount = 1;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &colorAttachmentInfo;
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo);
currentGraphicsPipeline = VK_NULL_HANDLE;
startRenderPass(nullptr, swapChainExtent.width, swapChainExtent.height);
}
void Graphics::endRecordingGraphicsCommands() {
const auto& commandBuffer = commandBuffers.at(imageIndex);
vkCmdEndRendering(commandBuffer);
endRenderPass();
Vulkan::cmdTransitionImageLayout(commandBuffer, swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), swapChainImages[imageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
throw love::Exception("failed to record command buffer");
@@ -1202,6 +1198,9 @@ namespace love {
configuration.blendState = states.back().blend;
configuration.winding = states.back().winding;
configuration.cullmode = cullmode;
configuration.framebufferFormat = currentFramebufferOutputFormat;
configuration.viewportWidth = currentViewportWidth;
configuration.viewportHeight = currentViewportHeight;
std::vector<VkBuffer> bufferVector;
std::vector<VkDeviceSize> offsets;
@@ -1234,6 +1233,56 @@ namespace love {
vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, static_cast<uint32_t>(bufferVector.size()), bufferVector.data(), offsets.data());
}
void Graphics::startRenderPass(Texture* texture, uint32_t w, uint32_t h) {
VkRenderingAttachmentInfo colorAttachmentInfo{};
colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
if (texture) {
colorAttachmentInfo.imageView = texture->getImageView();
colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; // fixme: we want to clear a canvas sometimes.
auto vulkanFormat = Vulkan::getTextureFormat(texture->getPixelFormat());
currentFramebufferOutputFormat = vulkanFormat.internalFormat;
renderTargetTexture = texture;
} else {
colorAttachmentInfo.imageView = swapChainImageViews[imageIndex];
colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
currentFramebufferOutputFormat = swapChainImageFormat;
renderTargetTexture = nullptr;
}
colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachmentInfo.clearValue = clearColor;
VkRenderingInfo renderingInfo{};
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
renderingInfo.renderArea.extent.width = w;
renderingInfo.renderArea.extent.height = h;
renderingInfo.layerCount = 1;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &colorAttachmentInfo;
currentViewportWidth = (float)w;
currentViewportHeight = (float)h;
if (renderTargetTexture) {
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)texture->getHandle(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
vkCmdBeginRendering(commandBuffers.at(imageIndex), &renderingInfo);
currentGraphicsPipeline = VK_NULL_HANDLE;
}
void Graphics::endRenderPass() {
vkCmdEndRendering(commandBuffers.at(imageIndex));
if (renderTargetTexture) {
Vulkan::cmdTransitionImageLayout(commandBuffers.at(imageIndex), (VkImage)renderTargetTexture->getHandle(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
renderTargetTexture = nullptr;
}
}
VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
auto shader = configuration.shader;
auto shaderStages = shader->getShaderStages();
@@ -1253,8 +1302,8 @@ namespace love {
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)swapChainExtent.width;
viewport.height = (float)swapChainExtent.height;
viewport.width = configuration.viewportWidth;
viewport.height = configuration.viewportHeight;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
@@ -1323,10 +1372,12 @@ namespace love {
}
graphicsPipelineLayouts.push_back(pipelineLayout);
VkFormat framebufferOutputFormat = configuration.framebufferFormat;
VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{};
pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
pipelineRenderingCreateInfo.colorAttachmentCount = 1;
pipelineRenderingCreateInfo.pColorAttachmentFormats = &swapChainImageFormat;
pipelineRenderingCreateInfo.pColorAttachmentFormats = &framebufferOutputFormat;
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
@@ -1440,6 +1491,15 @@ namespace love {
}
bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other) {
if (first.viewportHeight != other.viewportHeight) {
return false;
}
if (first.viewportWidth != other.viewportWidth) {
return false;
}
if (first.framebufferFormat != other.framebufferFormat) {
return false;
}
if (first.cullmode != other.cullmode) {
return false;
}
+9 -1
View File
@@ -29,6 +29,9 @@ namespace love {
ColorChannelMask colorChannelMask;
Winding winding;
CullMode cullmode;
VkFormat framebufferFormat;
float viewportWidth;
float viewportHeight;
friend static bool operator==(const GraphicsPipelineConfiguration& first, const GraphicsPipelineConfiguration& other);
};
@@ -174,6 +177,8 @@ namespace love {
graphics::StreamBuffer* getUniformBuffer();
void createVulkanVertexFormat(VertexAttributes vertexAttributes, bool& useConstantVertexColor, GraphicsPipelineConfiguration& configuration);
void prepareDraw(const VertexAttributes& attributes, const BufferBindings& buffers, graphics::Texture* texture, PrimitiveType, CullMode);
void startRenderPass(Texture*, uint32_t w, uint32_t h);
void endRenderPass();
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
@@ -213,7 +218,10 @@ namespace love {
std::vector<std::pair<DecriptorSetConfiguration, std::vector<VkDescriptorSet>>> descriptorSetsMap;
VkPolygonMode currentPolygonMode = VK_POLYGON_MODE_FILL;
VkCommandBuffer offscreenRendertargetCommandBuffer;
VkFormat currentFramebufferOutputFormat = VK_FORMAT_UNDEFINED;
Texture* renderTargetTexture = nullptr;
float currentViewportWidth = 0;
float currentViewportHeight = 0;
};
}
}
+10 -4
View File
@@ -19,6 +19,12 @@ namespace love {
auto vulkanFormat = Vulkan::getTextureFormat(format);
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
if (isRenderTarget()) {
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
@@ -30,7 +36,7 @@ namespace love {
imageInfo.format = vulkanFormat.internalFormat;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.usage = usageFlags;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
@@ -39,7 +45,6 @@ namespace love {
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS) {
throw love::Exception("failed to create image");
}
// fixme: is there a way we don't use the general image layout?
transitionImageLayout(textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
if (data) {
@@ -62,7 +67,7 @@ namespace love {
defaultPixels.push_back(255);
defaultPixels.push_back(255);
defaultPixels.push_back(255);
defaultPixels.push_back(0);
defaultPixels.push_back(255);
}
Rect rect = { 0, 0, width, height };
uploadByteData(PIXELFORMAT_RGBA8_UNORM, defaultPixels.data(), defaultPixels.size(), 0, 0, rect);
@@ -174,7 +179,8 @@ namespace love {
static_cast<uint32_t>(r.w),
static_cast<uint32_t>(r.h), 1
};
// fixme: we should use VK_IMAGE_LAYOUT_DST_OPTIMAL for transfer
vkCmdCopyBufferToImage(
commandBuffer,
buffer,
+15 -2
View File
@@ -279,8 +279,7 @@ namespace love {
std::string Vulkan::getVulkanApiVersion(uint32_t version) {
std::stringstream ss;
ss << VK_API_VERSION_VARIANT(version)
<< "." << VK_API_VERSION_MAJOR(version)
ss << "." << VK_API_VERSION_MAJOR(version)
<< "." << VK_API_VERSION_MINOR(version)
<< "." << VK_API_VERSION_PATCH(version);
@@ -458,6 +457,20 @@ namespace love {
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_GENERAL && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_GENERAL) {
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = 0;
sourceStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
destinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}
else {
throw std::invalid_argument("unsupported layout transition!");
}