JBR-7565 Vulkan: Implement clip

(cherry picked from commit 55787a5c2d)
This commit is contained in:
Nikita Gubarkov
2024-09-06 15:00:15 +02:00
committed by jbrbot
parent 364a1f3452
commit 936d7da2c0
10 changed files with 321 additions and 51 deletions

View File

@@ -0,0 +1,11 @@
#version 450
layout(push_constant) uniform PushConstants {
vec2 viewportNormalizer; // 2.0 / viewport
} push;
layout(location = 0) in ivec2 in_Position;
void main() {
gl_Position = vec4(vec2(in_Position) * push.viewportNormalizer - vec2(1.0), 0.0, 1.0);
}

View File

@@ -40,7 +40,7 @@ static VkBool32 VKImage_CreateView(VKDevice* device, VKImage* image) {
.image = image->handle,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = image->format,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.aspectMask = VKImage_GetAspect(image),
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = 1,
.subresourceRange.baseArrayLayer = 0,
@@ -53,6 +53,11 @@ static VkBool32 VKImage_CreateView(VKDevice* device, VKImage* image) {
return VK_TRUE;
}
VkImageAspectFlagBits VKImage_GetAspect(VKImage* image) {
return VKUtil_GetFormatGroup(image->format).bytes == 0 ? // Unknown format group means stencil.
VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
}
VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
VkImageCreateFlags flags, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage, VkSampleCountFlagBits samples,

View File

@@ -42,6 +42,8 @@ struct VKImage {
VkAccessFlagBits lastAccess;
};
VkImageAspectFlagBits VKImage_GetAspect(VKImage* image);
VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
VkImageCreateFlags flags, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage, VkSampleCountFlagBits samples,

View File

@@ -109,6 +109,7 @@ for (uint32_t i = 0; i < SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME); i++) {
typedef struct {
VkGraphicsPipelineCreateInfo createInfo;
VkPipelineMultisampleStateCreateInfo multisampleState;
VkPipelineDepthStencilStateCreateInfo depthStencilState;;
VkPipelineColorBlendStateCreateInfo colorBlendState;
VkPipelineDynamicStateCreateInfo dynamicState;
VkDynamicState dynamicStates[2];
@@ -148,6 +149,20 @@ static void VKPipelines_InitPipelineCreateState(PipelineCreateState* state) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
const VkStencilOpState stencilOpState = {
.failOp = VK_STENCIL_OP_KEEP,
.passOp = VK_STENCIL_OP_KEEP,
.compareOp = VK_COMPARE_OP_NOT_EQUAL,
.compareMask = 0xFFFFFFFFU,
.writeMask = 0U,
.reference = CLIP_STENCIL_EXCLUDE_VALUE
};
state->depthStencilState = (VkPipelineDepthStencilStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.stencilTestEnable = VK_FALSE,
.front = stencilOpState,
.back = stencilOpState
};
state->colorBlendState = (VkPipelineColorBlendStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
@@ -168,6 +183,7 @@ static void VKPipelines_InitPipelineCreateState(PipelineCreateState* state) {
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationState,
.pMultisampleState = &state->multisampleState,
.pDepthStencilState = &state->depthStencilState,
.pColorBlendState = &state->colorBlendState,
.pDynamicState = &state->dynamicState,
.subpass = 0,
@@ -245,9 +261,10 @@ static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderP
PipelineCreateState base;
VKPipelines_InitPipelineCreateState(&base);
base.createInfo.layout = pipelineContext->pipelineLayout;
base.createInfo.renderPass = renderPassContext->renderPass;
base.createInfo.renderPass = renderPassContext->renderPass[descriptor.stencilMode != STENCIL_MODE_NONE];
base.colorBlendState.pAttachments = &COMPOSITE_BLEND_STATES[descriptor.composite];
if (COMPOSITE_GROUP(descriptor.composite) == LOGIC_COMPOSITE_GROUP) base.colorBlendState.logicOpEnable = VK_TRUE;
if (descriptor.stencilMode == STENCIL_MODE_ON) base.depthStencilState.stencilTestEnable = VK_TRUE;
assert(base.dynamicState.dynamicStateCount <= SARRAY_COUNT_OF(base.dynamicStates));
ShaderStages stages[PIPELINE_COUNT];
@@ -282,13 +299,14 @@ static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderP
// TODO pipeline cache
VK_IF_ERROR(device->vkCreateGraphicsPipelines(device->handle, VK_NULL_HANDLE, PIPELINE_COUNT,
createInfos, NULL, set->pipelines)) VK_UNHANDLED_ERROR();
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet: composite=%d", descriptor.composite);
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet: composite=%d, stencilMode=%d",
descriptor.composite, descriptor.stencilMode);
return set;
}
static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext* renderPassContext) {
static VkResult VKPipelines_InitRenderPasses(VKDevice* device, VKRenderPassContext* renderPassContext) {
assert(device != NULL && renderPassContext != NULL);
VkAttachmentDescription colorAttachment = {
VkAttachmentDescription attachments[] = {{
.format = renderPassContext->format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
@@ -297,36 +315,47 @@ static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
}, {
.format = VK_FORMAT_S8_UINT,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
}};
VkAttachmentReference colorReference = {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
VkAttachmentReference stencilReference = {
.attachment = 1,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
};
VkSubpassDescription subpassDescription = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
.pColorAttachments = &colorReference
};
// TODO this is probably not needed?
// // Subpass dependencies for layout transitions
// VkSubpassDependency dependency = {
// .srcSubpass = VK_SUBPASS_EXTERNAL,
// .dstSubpass = 0,
// .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
// .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
// .srcAccessMask = 0,
// .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
// };
VkRenderPassCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &colorAttachment,
.pAttachments = attachments,
.subpassCount = 1,
.pSubpasses = &subpassDescription,
.dependencyCount = 0,
.pDependencies = NULL
};
return device->vkCreateRenderPass(device->handle, &createInfo, NULL, &renderPassContext->renderPass);
for (uint32_t i = 0; i < 2; i++) {
if (i == 1) {
createInfo.attachmentCount = 2;
subpassDescription.pDepthStencilAttachment = &stencilReference;
}
VkResult result = device->vkCreateRenderPass(device->handle, &createInfo, NULL, &renderPassContext->renderPass[i]);
VK_IF_ERROR(result) return result;
}
return VK_SUCCESS;
}
static void VKPipelines_DestroyRenderPassContext(VKRenderPassContext* renderPassContext) {
@@ -342,7 +371,10 @@ static void VKPipelines_DestroyRenderPassContext(VKRenderPassContext* renderPass
}
}
ARRAY_FREE(renderPassContext->pipelineSets);
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass, NULL);
device->vkDestroyPipeline(device->handle, renderPassContext->clipPipeline, NULL);
for (uint32_t i = 0; i < 2; i++) {
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass[i], NULL);
}
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_DestroyRenderPassContext(%p): format=%d",
renderPassContext, renderPassContext->format);
free(renderPassContext);
@@ -355,11 +387,50 @@ static VKRenderPassContext* VKPipelines_CreateRenderPassContext(VKPipelineContex
renderPassContext->pipelineContext = pipelineContext;
renderPassContext->format = format;
VK_IF_ERROR(VKPipelines_InitRenderPass(pipelineContext->device, renderPassContext)) {
VK_IF_ERROR(VKPipelines_InitRenderPasses(pipelineContext->device, renderPassContext)) {
VKPipelines_DestroyRenderPassContext(renderPassContext);
return NULL;
}
// Setup default pipeline parameters.
const VkPipelineColorBlendAttachmentState NO_COLOR_ATTACHMENT = {
.blendEnable = VK_FALSE,
.colorWriteMask = 0
};
PipelineCreateState base;
VKPipelines_InitPipelineCreateState(&base);
base.createInfo.layout = pipelineContext->pipelineLayout;
base.createInfo.renderPass = renderPassContext->renderPass[1];
base.colorBlendState.pAttachments = &NO_COLOR_ATTACHMENT;
base.depthStencilState.stencilTestEnable = VK_TRUE;
assert(base.dynamicState.dynamicStateCount <= SARRAY_COUNT_OF(base.dynamicStates));
// Setup clip pipeline.
MAKE_INPUT_STATE(CLIP, VKIntVertex, VK_FORMAT_R32G32_SINT);
base.createInfo.pVertexInputState = &INPUT_STATE_CLIP;
base.createInfo.pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_LIST;
const VkStencilOpState CLIP_STENCIL_OP = {
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_NEVER,
.compareMask = 0U,
.writeMask = 0xFFFFFFFFU,
.reference = CLIP_STENCIL_INCLUDE_VALUE
};
const VkPipelineDepthStencilStateCreateInfo CLIP_STENCIL_STATE = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.stencilTestEnable = VK_TRUE,
.front = CLIP_STENCIL_OP,
.back = CLIP_STENCIL_OP
};
base.createInfo.pDepthStencilState = &CLIP_STENCIL_STATE;
base.createInfo.stageCount = 1;
base.createInfo.pStages = &pipelineContext->shaders->clip_vert;
// Create pipelines.
// TODO pipeline cache
VK_IF_ERROR(pipelineContext->device->vkCreateGraphicsPipelines(
pipelineContext->device->handle, VK_NULL_HANDLE, 1, &base.createInfo, NULL, &renderPassContext->clipPipeline)) VK_UNHANDLED_ERROR();
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_CreateRenderPassContext(%p): format=%d", renderPassContext, format);
return renderPassContext;
}
@@ -504,11 +575,13 @@ inline void hash(uint32_t* result, int i) { // Good for hashing enums.
}
inline uint32_t pipelineSetDescriptorHash(VKPipelineSetDescriptor* d) {
uint32_t h = 0U;
hash(&h, d->stencilMode);
hash(&h, d->composite);
return h;
}
inline VkBool32 pipelineSetDescriptorEquals(VKPipelineSetDescriptor* a, VKPipelineSetDescriptor* b) {
return a->composite == b->composite;
return a->stencilMode == b->stencilMode &&
a->composite == b->composite;
}
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipelineSetDescriptor descriptor, VKPipeline pipeline) {

View File

@@ -27,6 +27,9 @@
#include "java_awt_AlphaComposite.h"
#include "VKTypes.h"
#define CLIP_STENCIL_INCLUDE_VALUE 0x80U
#define CLIP_STENCIL_EXCLUDE_VALUE 0U
/**
* All pipeline types.
*/
@@ -39,6 +42,12 @@ typedef enum {
NO_PIPELINE = 0x7FFFFFFF
} VKPipeline;
typedef enum {
STENCIL_MODE_NONE = 0, // No stencil attachment.
STENCIL_MODE_OFF = 1, // Has stencil attachment, stencil test disabled.
STENCIL_MODE_ON = 2 // Has stencil attachment, stencil test enabled.
} VKStencilMode;
/**
* There are two groups of composite modes:
* - Logic composite - using logicOp.
@@ -93,7 +102,8 @@ struct VKPipelineContext {
struct VKRenderPassContext {
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass;
VkRenderPass renderPass[2]; // Color-only and color+stencil.
VkPipeline clipPipeline;
struct VKPipelineSet** pipelineSets;
};
@@ -102,9 +112,14 @@ struct VKRenderPassContext {
* When adding new fields, update hash and comparison in VKPipelines_GetPipeline.
*/
typedef struct {
VKStencilMode stencilMode;
VKCompositeMode composite;
} VKPipelineSetDescriptor;
typedef struct {
int x, y;
} VKIntVertex;
typedef struct {
float x, y;
Color color;

View File

@@ -93,15 +93,19 @@
#define OFFSET_XFORM sun_java2d_vulkan_VKBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_vulkan_VKBlitLoops_OFFSET_ISOBLIT
#define NO_CLIP ((VkRect2D) {{0, 0}, {0x7FFFFFFFU, 0x7FFFFFFFU}})
// Rendering context is only accessed from VKRenderQueue_flushBuffer,
// which is only called from queue flusher thread, no need for synchronization.
static VKRenderingContext context = {
.surface = NULL,
.transform = {1.0, 0.0, 0.0,0.0, 1.0, 0.0},
.clipRect = {{0, 0},{INT_MAX, INT_MAX}},
.color = {},
.composite = ALPHA_COMPOSITE_SRC_OVER,
.extraAlpha = 1.0f
.extraAlpha = 1.0f,
.clipModCount = 1,
.clipRect = NO_CLIP,
.clipSpanVertices = NULL
};
// We keep this color separately from context.color,
// because we need consistent state when switching between XOR and alpha composite modes.
@@ -435,12 +439,17 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_RECT_CLIP(%d, %d, %d, %d)",
x1, y1, x2, y2);
ARRAY_RESIZE(context.clipSpanVertices, 0);
context.clipRect = (VkRect2D) {{x1, y1}, {x2 - x1, y2 - y1}};
context.clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_BEGIN_SHAPE_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: BEGIN_SHAPE_CLIP");
ARRAY_RESIZE(context.clipSpanVertices, 0);
context.clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
@@ -448,19 +457,38 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint count = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SHAPE_CLIP_SPANS");
SKIP_BYTES(b, count * BYTES_PER_SPAN);
size_t offset = ARRAY_SIZE(context.clipSpanVertices);
ARRAY_RESIZE(context.clipSpanVertices, offset + count * 6);
for (jint i = 0; i < count; i++) {
jint x1 = NEXT_INT(b);
jint y1 = NEXT_INT(b);
jint x2 = NEXT_INT(b);
jint y2 = NEXT_INT(b);
context.clipSpanVertices[offset + i * 6 + 0] = (VKIntVertex) {x1, y1};
context.clipSpanVertices[offset + i * 6 + 1] = (VKIntVertex) {x2, y1};
context.clipSpanVertices[offset + i * 6 + 2] = (VKIntVertex) {x2, y2};
context.clipSpanVertices[offset + i * 6 + 3] = (VKIntVertex) {x2, y2};
context.clipSpanVertices[offset + i * 6 + 4] = (VKIntVertex) {x1, y2};
context.clipSpanVertices[offset + i * 6 + 5] = (VKIntVertex) {x1, y1};
}
context.clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_END_SHAPE_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: END_SHAPE_CLIP");
context.clipRect = NO_CLIP;
context.clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_CLIP");
ARRAY_RESIZE(context.clipSpanVertices, 0);
context.clipRect = NO_CLIP;
context.clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:

View File

@@ -139,6 +139,7 @@ struct VKRenderPass {
VKRenderPassContext* context;
VKBuffer* vertexBuffers;
VKTexelBuffer* maskFillBuffers;
VkRenderPass renderPass; // Non-owning.
VkFramebuffer framebuffer;
VkCommandBuffer commandBuffer;
@@ -149,6 +150,8 @@ struct VKRenderPass {
VKCompositeMode currentComposite;
VKPipeline currentPipeline;
VKStencilMode currentStencilMode;
uint64_t clipModCount; // Just a tag to detect when clip was changed.
VkBool32 pendingFlush;
VkBool32 pendingCommands;
VkBool32 pendingClear;
@@ -478,7 +481,7 @@ void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch*
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image->handle,
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
.subresourceRange = { VKImage_GetAspect(image), 0, 1, 0, 1 }
};
batch->barrierCount++;
batch->srcStages |= image->lastStage;
@@ -584,10 +587,13 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
VKRenderPass* renderPass = surface->renderPass = malloc(sizeof(VKRenderPass));
VK_RUNTIME_ASSERT(renderPass);
(*renderPass) = (VKRenderPass) {
.pendingCommands = VK_FALSE,
.pendingClear = VK_TRUE, // Clear the surface by default
.currentComposite = NO_COMPOSITE,
.currentPipeline = NO_PIPELINE,
.currentStencilMode = STENCIL_MODE_NONE,
.clipModCount = 0,
.pendingFlush = VK_FALSE,
.pendingCommands = VK_FALSE,
.pendingClear = VK_TRUE, // Clear the surface by default
.lastTimestamp = 0
};
@@ -596,23 +602,48 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
renderPass->context = VKPipelines_GetRenderPassContext(renderer->pipelineContext, surface->image->format);
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
return VK_TRUE;
}
/**
* Initialize surface framebuffer.
* This function can be called between render passes of a single frame, unlike VKRenderer_InitRenderPass.
*/
static void VKRenderer_InitFramebuffer(VKSDOps* surface) {
assert(surface != NULL && surface->device != NULL && surface->renderPass != NULL);
VKDevice* device = surface->device;
VKRenderPass* renderPass = surface->renderPass;
if (renderPass->currentStencilMode == STENCIL_MODE_NONE && surface->stencil != NULL) {
// Destroy outdated color-only framebuffer.
device->vkDestroyFramebuffer(device->handle, renderPass->framebuffer, NULL);
renderPass->framebuffer = VK_NULL_HANDLE;
renderPass->currentStencilMode = STENCIL_MODE_OFF;
}
// Initialize framebuffer.
if (renderPass->framebuffer == VK_NULL_HANDLE) {
renderPass->renderPass = renderPass->context->renderPass[surface->stencil != NULL];
VkImageView views[] = { surface->image->view, VK_NULL_HANDLE };
VkFramebufferCreateInfo framebufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = renderPass->context->renderPass,
.renderPass = renderPass->renderPass,
.attachmentCount = 1,
.pAttachments = &surface->image->view,
.pAttachments = views,
.width = surface->image->extent.width,
.height = surface->image->extent.height,
.layers = 1
};
if (surface->stencil != NULL) {
framebufferCreateInfo.attachmentCount = 2;
views[1] = surface->stencil->view;
}
VK_IF_ERROR(device->vkCreateFramebuffer(device->handle, &framebufferCreateInfo, NULL,
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
return VK_TRUE;
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitFramebuffer(%p)", surface);
}
}
/**
@@ -620,6 +651,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
*/
static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL && !surface->renderPass->pendingCommands);
VKRenderer_InitFramebuffer(surface);
// We may have a pending flush, which is already obsolete.
surface->renderPass->pendingFlush = VK_FALSE;
VKDevice* device = surface->device;
@@ -646,7 +678,7 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
// Begin recording render pass commands.
VkCommandBufferInheritanceInfo inheritanceInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
.renderPass = surface->renderPass->context->renderPass,
.renderPass = surface->renderPass->renderPass,
.subpass = 0,
.framebuffer = surface->renderPass->framebuffer
};
@@ -675,7 +707,7 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
surface->renderPass->pendingClear = VK_FALSE;
}
// Set viewport and scissor.
// Set viewport.
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
@@ -684,9 +716,7 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
.minDepth = 0.0f,
.maxDepth = 1.0f
};
VkRect2D scissor = {{0, 0}, surface->image->extent};
device->vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
device->vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// Calculate inverse viewport for vertex shader.
viewport.width = 2.0f / viewport.width;
viewport.height = 2.0f / viewport.height;
@@ -717,22 +747,30 @@ void VKRenderer_FlushRenderPass(VKSDOps* surface) {
surface->renderPass->lastTimestamp = renderer->writeTimestamp;
VkCommandBuffer cb = VKRenderer_Record(renderer);
// Insert barrier to prepare surface for rendering.
VkImageMemoryBarrier barriers[1];
// Insert barriers to prepare surface for rendering.
VkImageMemoryBarrier barriers[2];
VKBarrierBatch barrierBatch = {};
VKRenderer_AddImageBarrier(barriers, &barrierBatch, surface->image,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (surface->stencil != NULL) {
VKRenderer_AddImageBarrier(barriers, &barrierBatch, surface->stencil,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
if (barrierBatch.barrierCount > 0) {
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
0, 0, NULL, 0, NULL, barrierBatch.barrierCount, barriers);
}
// If there is a pending clear, record it into render pass.
if (clear) VKRenderer_BeginRenderPass(surface);
// Begin render pass.
VkRenderPassBeginInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = surface->renderPass->context->renderPass,
.renderPass = surface->renderPass->renderPass,
.framebuffer = surface->renderPass->framebuffer,
.renderArea.offset = (VkOffset2D){0, 0},
.renderArea.extent = surface->image->extent,
@@ -740,8 +778,6 @@ void VKRenderer_FlushRenderPass(VKSDOps* surface) {
.pClearValues = NULL
};
device->vkCmdBeginRenderPass(cb, &renderPassInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
// If there is a pending clear, record it into render pass.
if (clear) VKRenderer_BeginRenderPass(surface);
// Execute render pass commands.
if (surface->renderPass->pendingCommands) {
@@ -961,6 +997,54 @@ static BufferWritingState VKRenderer_AllocateMaskFillBytes(VKRenderingContext* c
return state;
}
/**
* Setup stencil attachment according to the context clip state.
* If there is a clip shape, attachment is cleared with "fail" value and then
* pixels inside the clip shape are set to "pass".
* If there is no clip shape, whole attachment is cleared with "pass" value.
*/
static void VKRenderer_SetupStencil(VKRenderingContext* context) {
assert(context != NULL && context->surface != NULL && context->surface->renderPass != NULL);
VKSDOps* surface = context->surface;
VKRenderPass* renderPass = surface->renderPass;
VkCommandBuffer cb = renderPass->commandBuffer;
VKRenderer_FlushDraw(surface);
// Clear stencil attachment.
VkClearAttachment clearAttachment = {
.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT,
.clearValue.depthStencil.stencil = ARRAY_SIZE(context->clipSpanVertices) > 0 ?
CLIP_STENCIL_EXCLUDE_VALUE : CLIP_STENCIL_INCLUDE_VALUE
};
VkClearRect clearRect = {
.rect = {{0, 0}, surface->stencil->extent},
.baseArrayLayer = 0,
.layerCount = 1
};
surface->device->vkCmdClearAttachments(cb, 1, &clearAttachment, 1, &clearRect);
// Bind the clip pipeline.
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, surface->renderPass->context->clipPipeline);
// Reset vertex buffer binding.
renderPass->vertexBufferWriting.bound = VK_FALSE;
// Rasterize clip spans.
const uint32_t MAX_VERTICES_PER_DRAW = (VERTEX_BUFFER_SIZE / sizeof(VKIntVertex) / 3) * 3;
VKIntVertex* vs;
for (uint32_t drawn = 0;;) {
uint32_t currentDraw = ARRAY_SIZE(context->clipSpanVertices) - drawn;
if (currentDraw > MAX_VERTICES_PER_DRAW) currentDraw = MAX_VERTICES_PER_DRAW;
else if (currentDraw == 0) break;
VK_DRAW(vs, context, currentDraw);
memcpy(vs, context->clipSpanVertices + drawn, currentDraw * sizeof(VKIntVertex));
drawn += currentDraw;
}
VKRenderer_FlushDraw(surface);
// Reset pipeline state.
renderPass->currentPipeline = NO_PIPELINE;
}
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
@@ -977,19 +1061,41 @@ VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
VKRenderPass* renderPass = surface->renderPass;
// Validate render pass state.
if (renderPass->currentComposite != context->composite) {
if (renderPass->currentComposite != context->composite ||
renderPass->clipModCount != context->clipModCount) {
// ALPHA_COMPOSITE_DST keeps destination intact, so don't even bother to change the state.
if (context->composite == ALPHA_COMPOSITE_DST) return VK_FALSE;
VKCompositeMode oldComposite = renderPass->currentComposite;
VkBool32 clipChanged = renderPass->clipModCount != context->clipModCount;
// Init stencil attachment, if needed.
if (clipChanged && ARRAY_SIZE(context->clipSpanVertices) > 0 && surface->stencil == NULL) {
if (surface->renderPass->pendingCommands) VKRenderer_FlushRenderPass(surface);
if (!VKSD_ConfigureImageSurfaceStencil(surface)) return VK_FALSE;
}
// Update state.
VKRenderer_FlushDraw(surface);
renderPass->currentComposite = context->composite;
renderPass->clipModCount = context->clipModCount;
// Begin render pass.
if (!renderPass->pendingCommands) VKRenderer_BeginRenderPass(surface);
VkBool32 renderPassJustStarted = !renderPass->pendingCommands;
if (renderPassJustStarted) VKRenderer_BeginRenderPass(surface);
// Validate current clip.
if (clipChanged || renderPassJustStarted) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating clip");
surface->device->vkCmdSetScissor(renderPass->commandBuffer, 0, 1, &context->clipRect);
if (clipChanged) {
if (ARRAY_SIZE(context->clipSpanVertices) > 0) {
VKRenderer_SetupStencil(context);
renderPass->currentStencilMode = STENCIL_MODE_ON;
} else renderPass->currentStencilMode = surface->stencil != NULL ? STENCIL_MODE_OFF : STENCIL_MODE_NONE;
}
}
// Validate current composite.
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context->composite);
// Reset the pipeline.
renderPass->currentPipeline = NO_PIPELINE;
if (oldComposite != context->composite) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context->composite);
// Reset the pipeline.
renderPass->currentPipeline = NO_PIPELINE;
}
}
// Validate current pipeline.
@@ -1000,7 +1106,8 @@ VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->currentPipeline = pipeline;
VKPipelineSetDescriptor pipelineSetDescriptor = {
.composite = context->composite
.stencilMode = renderPass->currentStencilMode,
.composite = renderPass->currentComposite
};
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
VKPipelines_GetPipeline(renderPass->context, pipelineSetDescriptor, pipeline));

View File

@@ -33,12 +33,14 @@
struct VKRenderingContext {
VKSDOps* surface;
VKTransform transform;
VkRect2D clipRect;
Color color;
VKCompositeMode composite;
// Extra alpha is not used when painting with plain color,
// in this case color.a already includes it.
float extraAlpha;
uint64_t clipModCount; // Used to track changes to the clip.
VkRect2D clipRect;
VKIntVertex* clipSpanVertices;
};
typedef struct {

View File

@@ -40,9 +40,10 @@ static void VKSD_ResetImageSurface(VKSDOps* vksdo) {
// DestroyRenderPass also waits while the surface resources are being used by device.
VKRenderer_DestroyRenderPass(vksdo);
if (vksdo->device != NULL && vksdo->image != NULL) {
if (vksdo->device != NULL) {
VKImage_Destroy(vksdo->device, vksdo->stencil);
VKImage_Destroy(vksdo->device, vksdo->image);
vksdo->image = NULL;
vksdo->image = vksdo->stencil = NULL;
}
}
@@ -100,6 +101,25 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
return vksdo->image != NULL;
}
VkBool32 VKSD_ConfigureImageSurfaceStencil(VKSDOps* vksdo) {
// Check that image is ready.
if (vksdo->image == NULL) {
J2dRlsTraceLn1(J2D_TRACE_WARNING, "VKSD_ConfigureImageSurfaceStencil(%p): image is not ready", vksdo);
return VK_FALSE;
}
// Initialize stencil image.
if (vksdo->stencil == NULL) {
vksdo->stencil = VKImage_Create(vksdo->device, vksdo->image->extent.width, vksdo->image->extent.height,
0, VK_FORMAT_S8_UINT, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_SAMPLE_COUNT_1_BIT, VKSD_FindImageSurfaceMemoryType);
VK_RUNTIME_ASSERT(vksdo->stencil);
J2dRlsTraceLn3(J2D_TRACE_INFO, "VKSD_ConfigureImageSurfaceStencil(%p): stencil image updated %dx%d",
vksdo, vksdo->stencil->extent.width, vksdo->stencil->extent.height);
}
return vksdo->stencil != NULL;
}
VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
// Check that image is ready.
if (vkwinsdo->vksdOps.image == NULL) {

View File

@@ -50,6 +50,7 @@ struct VKSDOps {
jint drawableType;
VKDevice* device;
VKImage* image;
VKImage* stencil;
Color background;
VkExtent2D requestedExtent;
@@ -84,6 +85,12 @@ void VKSD_ResetSurface(VKSDOps* vksdo);
*/
VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo);
/**
* [Re]configure stencil attachment of the image surface.
* VKSD_ConfigureImageSurface must have been called before.
*/
VkBool32 VKSD_ConfigureImageSurfaceStencil(VKSDOps* vksdo);
/**
* Configure window surface. This [re]initializes the swapchain.
* VKSD_ConfigureImageSurface must have been called before.