Skip to content

Commit

Permalink
JBR-7565 Vulkan: Implement clip
Browse files Browse the repository at this point in the history
  • Loading branch information
YaaZ authored and jbrbot committed Oct 8, 2024
1 parent edf3cc2 commit af16146
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 49 deletions.
11 changes: 11 additions & 0 deletions src/java.desktop/share/glsl/vulkan/clip.vert
Original file line number Diff line number Diff line change
@@ -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);
}
7 changes: 6 additions & 1 deletion src/java.desktop/share/native/common/java2d/vulkan/VKImage.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/java.desktop/share/native/common/java2d/vulkan/VKImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
113 changes: 93 additions & 20 deletions src/java.desktop/share/native/common/java2d/vulkan/VKPipelines.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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,
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down
17 changes: 16 additions & 1 deletion src/java.desktop/share/native/common/java2d/vulkan/VKPipelines.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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.
Expand Down Expand Up @@ -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;
};

Expand All @@ -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;
Expand Down
33 changes: 31 additions & 2 deletions src/java.desktop/share/native/common/java2d/vulkan/VKRenderQueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
#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 = {
Expand All @@ -101,7 +103,10 @@ static VKRenderingContext context = {
.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.
Expand Down Expand Up @@ -435,32 +440,56 @@ 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:
{
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:
Expand Down
Loading

0 comments on commit af16146

Please sign in to comment.