mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-21 16:59:41 +01:00
11
src/java.desktop/share/glsl/vulkan/clip.vert
Normal file
11
src/java.desktop/share/glsl/vulkan/clip.vert
Normal 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);
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user