JBR-7575 Vulkan: Implement composites (blending and XOR mode)

- Implemented dynamic pipeline compilation.
- Added 64-bit per pixel format usage in debug mode for testing.
- Now passing colors from Java to Vulkan with straight alpha.

(cherry picked from commit 3d7baad687)
This commit is contained in:
Nikita Gubarkov
2024-09-03 14:52:13 +02:00
parent 6ef6b1d328
commit 8e05c17afb
10 changed files with 237 additions and 111 deletions

View File

@@ -75,9 +75,11 @@ public abstract class VKSurfaceData extends SurfaceData
private static final String DESC_VK_TEXTURE = "VK Texture";
// We want non-premultiplied alpha to prevent precision loss, so use PixelConverter.Argb
// See also VKUtil_DecodeJavaColor.
static final SurfaceType VKSurface =
SurfaceType.Any.deriveSubType(DESC_VK_SURFACE,
PixelConverter.ArgbPre.instance);
PixelConverter.Argb.instance);
static final SurfaceType VKSurfaceRTT =
VKSurface.deriveSubType(DESC_VK_SURFACE_RTT);
static final SurfaceType VKTexture =

View File

@@ -33,15 +33,15 @@
#undef SHADER_ENTRY
#undef BYTECODE_END
struct VKPipelineSet {
typedef struct VKPipelineSet {
VkPipeline pipelines[PIPELINE_COUNT];
};
} VKPipelineSet;
struct VKShaders {
typedef struct VKShaders {
# define SHADER_ENTRY(NAME, TYPE) VkPipelineShaderStageCreateInfo NAME ## _ ## TYPE;
# include "vulkan/shader_list.h"
# undef SHADER_ENTRY
};
} VKShaders;
static void VKPipelines_DestroyShaders(VKDevice* device, VKShaders* shaders) {
assert(device != NULL);
@@ -79,19 +79,19 @@ static VKShaders* VKPipelines_CreateShaders(VKDevice* device) {
return shaders;
}
#define MAKE_INPUT_STATE(TYPE, ...) \
static const VkVertexInputAttributeDescription attributes[] = { __VA_ARGS__ }; \
static const VkVertexInputBindingDescription binding = { \
.binding = 0, \
.stride = sizeof(TYPE), \
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
}; \
static const VkPipelineVertexInputStateCreateInfo inputState = { \
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
.vertexBindingDescriptionCount = 1, \
.pVertexBindingDescriptions = &binding, \
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(attributes), \
.pVertexAttributeDescriptions = attributes \
#define MAKE_INPUT_STATE(NAME, TYPE, ...) \
static const VkVertexInputAttributeDescription INPUT_STATE_ATTRIBUTES_##NAME[] = { __VA_ARGS__ }; \
static const VkVertexInputBindingDescription INPUT_STATE_BINDING_##NAME = { \
.binding = 0, \
.stride = sizeof(TYPE), \
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
}; \
static const VkPipelineVertexInputStateCreateInfo INPUT_STATE_##NAME = { \
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
.vertexBindingDescriptionCount = 1, \
.pVertexBindingDescriptions = &INPUT_STATE_BINDING_##NAME, \
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME), \
.pVertexAttributeDescriptions = INPUT_STATE_ATTRIBUTES_##NAME \
}
typedef struct {
@@ -177,13 +177,62 @@ static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_LINE_LI
.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST
};
const VkPipelineColorBlendAttachmentState BLEND_STATE = {
.blendEnable = VK_FALSE,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT
// Blend states are hard-coded, but can also be loaded dynamically to implement custom composites.
#define DEF_BLEND(NAME, SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA) \
{ .blendEnable = VK_TRUE, \
.srcColorBlendFactor = VK_BLEND_FACTOR_ ## SRC_COLOR, \
.dstColorBlendFactor = VK_BLEND_FACTOR_ ## DST_COLOR, \
.colorBlendOp = VK_BLEND_OP_ADD, \
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ ## SRC_ALPHA, \
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ ## DST_ALPHA, \
.alphaBlendOp = VK_BLEND_OP_ADD, \
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | \
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }
const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT] = {
{ .blendEnable = VK_FALSE, // LOGIC_XOR
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
// NAME || SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
DEF_BLEND(| CLEAR |, ZERO , ZERO , ZERO , ZERO ),
DEF_BLEND(| SRC |, ONE , ZERO , ONE , ZERO ),
DEF_BLEND(| SRC_OVER |, ONE , ONE_MINUS_SRC_ALPHA , ONE , ONE_MINUS_SRC_ALPHA ),
DEF_BLEND(| DST_OVER |, ONE_MINUS_DST_ALPHA , ONE , ONE_MINUS_DST_ALPHA , ONE ),
DEF_BLEND(| SRC_IN |, DST_ALPHA , ZERO , DST_ALPHA , ZERO ),
DEF_BLEND(| DST_IN |, ZERO , SRC_ALPHA , ZERO , SRC_ALPHA ),
DEF_BLEND(| SRC_OUT |, ONE_MINUS_DST_ALPHA , ZERO , ONE_MINUS_DST_ALPHA , ZERO ),
DEF_BLEND(| DST_OUT |, ZERO , ONE_MINUS_SRC_ALPHA , ZERO , ONE_MINUS_SRC_ALPHA ),
DEF_BLEND(| DST |, ZERO , ONE , ZERO , ONE ),
DEF_BLEND(| SRC_ATOP |, DST_ALPHA , ONE_MINUS_SRC_ALPHA , ZERO , ONE ),
DEF_BLEND(| DST_ATOP |, ONE_MINUS_DST_ALPHA , SRC_ALPHA , ONE , ZERO ),
DEF_BLEND(| XOR |, ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA ),
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_POSITION = {
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = 0
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_COLOR = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = sizeof(float) * 2
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_TEXCOORD = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = sizeof(float) * 2
};
MAKE_INPUT_STATE(COLOR_VERTEX, VKColorVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_COLOR);
MAKE_INPUT_STATE(TEXCOORD_VERTEX, VKTxVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_TEXCOORD);
static void VKPipelines_DestroyPipelineSet(VKDevice* device, VKPipelineSet* set) {
assert(device != NULL);
if (set == NULL) return;
for (uint32_t i = 0; i < PIPELINE_COUNT; i++) {
device->vkDestroyPipeline(device->handle, set->pipelines[i], NULL);
@@ -191,8 +240,9 @@ static void VKPipelines_DestroyPipelineSet(VKDevice* device, VKPipelineSet* set)
free(set);
}
static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderPassContext) {
static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderPassContext, VKCompositeMode composite) {
assert(renderPassContext != NULL && renderPassContext->pipelineContext != NULL);
assert(composite < COMPOSITE_COUNT);
VKPipelineContext* pipelineContext = renderPassContext->pipelineContext;
VKPipelineSet* set = calloc(1, sizeof(VKPipelineSet));
@@ -205,7 +255,8 @@ static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderP
VKPipelines_InitPipelineCreateState(&base);
base.createInfo.layout = pipelineContext->pipelineLayout;
base.createInfo.renderPass = renderPassContext->renderPass;
base.colorBlendState.pAttachments = &BLEND_STATE;
base.colorBlendState.pAttachments = &COMPOSITE_BLEND_STATES[composite];
if (COMPOSITE_GROUP(composite) == LOGIC_COMPOSITE_GROUP) base.colorBlendState.logicOpEnable = VK_TRUE;
assert(base.dynamicState.dynamicStateCount <= SARRAY_COUNT_OF(base.dynamicStates));
ShaderStages stages[PIPELINE_COUNT];
@@ -215,36 +266,15 @@ static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderP
createInfos[i].pStages = stages[i].createInfos;
}
static const VkVertexInputAttributeDescription positionAttribute = {
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = 0
};
static const VkVertexInputAttributeDescription texcoordAttribute = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = sizeof(float) * 2
};
static const VkVertexInputAttributeDescription colorAttribute = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = sizeof(float) * 2
};
{ // Setup plain color pipelines.
MAKE_INPUT_STATE(VKColorVertex, positionAttribute, colorAttribute);
createInfos[PIPELINE_DRAW_COLOR].pVertexInputState = createInfos[PIPELINE_FILL_COLOR].pVertexInputState = &inputState;
createInfos[PIPELINE_DRAW_COLOR].pVertexInputState = createInfos[PIPELINE_FILL_COLOR].pVertexInputState = &INPUT_STATE_COLOR_VERTEX;
createInfos[PIPELINE_FILL_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_LIST;
createInfos[PIPELINE_DRAW_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_LINE_LIST;
stages[PIPELINE_DRAW_COLOR] = stages[PIPELINE_FILL_COLOR] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
}
{ // Setup texture pipeline.
MAKE_INPUT_STATE(VKTxVertex, positionAttribute, texcoordAttribute);
createInfos[PIPELINE_BLIT].pVertexInputState = &inputState;
createInfos[PIPELINE_BLIT].pVertexInputState = &INPUT_STATE_TEXCOORD_VERTEX;
createInfos[PIPELINE_BLIT].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_STRIP;
createInfos[PIPELINE_BLIT].layout = pipelineContext->texturePipelineLayout;
stages[PIPELINE_BLIT] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
@@ -254,11 +284,12 @@ 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();
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet");
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet: composite=%d", composite);
return set;
}
static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext* renderPassContext) {
assert(device != NULL && renderPassContext != NULL);
VkAttachmentDescription colorAttachment = {
.format = renderPassContext->format,
.samples = VK_SAMPLE_COUNT_1_BIT,
@@ -304,8 +335,13 @@ static void VKPipelines_DestroyRenderPassContext(VKRenderPassContext* renderPass
if (renderPassContext == NULL) return;
VKDevice* device = renderPassContext->pipelineContext->device;
assert(device != NULL);
VKPipelines_DestroyPipelineSet(device, renderPassContext->pipelineSet);
for (uint32_t i = 0; i < ARRAY_SIZE(renderPassContext->pipelineSets); i++) {
VKPipelines_DestroyPipelineSet(device, renderPassContext->pipelineSets[i]);
}
ARRAY_FREE(renderPassContext->pipelineSets);
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass, NULL);
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_DestroyRenderPassContext(%p): format=%d",
renderPassContext, renderPassContext->format);
free(renderPassContext);
}
@@ -321,6 +357,7 @@ static VKRenderPassContext* VKPipelines_CreateRenderPassContext(VKPipelineContex
return NULL;
}
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_CreateRenderPassContext(%p): format=%d", renderPassContext, format);
return renderPassContext;
}
@@ -397,6 +434,7 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device) {
return NULL;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreateContext(%p)", pipelineContext);
return pipelineContext;
}
@@ -417,13 +455,16 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_DestroyContext(%p)", pipelineContext);
free(pipelineContext);
}
VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format) {
assert(pipelineContext != NULL && pipelineContext->device != NULL);
for (uint32_t i = 0; i < ARRAY_SIZE(pipelineContext->renderPassContexts); i++) {
if (pipelineContext->renderPassContexts[i]->format == format) return pipelineContext->renderPassContexts[i];
if (pipelineContext->renderPassContexts[i]->format == format) {
return pipelineContext->renderPassContexts[i];
}
}
// Not found, create.
VKRenderPassContext* renderPassContext = VKPipelines_CreateRenderPassContext(pipelineContext, format);
@@ -431,9 +472,16 @@ VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelin
return renderPassContext;
}
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipeline pipeline) {
if (renderPassContext->pipelineSet == NULL) {
renderPassContext->pipelineSet = VKPipelines_CreatePipelineSet(renderPassContext);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline) {
assert(renderPassContext != NULL);
assert(composite < COMPOSITE_COUNT); // We could append custom composites after that index.
assert(pipeline < PIPELINE_COUNT); // We could append custom pipelines after that index.
// Currently, our pipelines map to composite modes 1-to-1, but this may change in future when we'll add more states.
uint32_t setIndex = (uint32_t) composite;
while (ARRAY_SIZE(renderPassContext->pipelineSets) <= setIndex) ARRAY_PUSH_BACK(renderPassContext->pipelineSets, NULL);
if (renderPassContext->pipelineSets[setIndex] == NULL) {
renderPassContext->pipelineSets[setIndex] = VKPipelines_CreatePipelineSet(renderPassContext, composite);
}
return renderPassContext->pipelineSet->pipelines[pipeline];
return renderPassContext->pipelineSets[setIndex]->pipelines[pipeline];
}

View File

@@ -24,22 +24,51 @@
#ifndef VKPipelines_h_Included
#define VKPipelines_h_Included
#include "java_awt_AlphaComposite.h"
#include "VKTypes.h"
typedef struct VKShaders VKShaders;
typedef struct VKPipelineSet VKPipelineSet;
/**
* All pipeline types, use these to index into VKPipelineSet.pipelines.
* All pipeline types.
*/
typedef enum {
PIPELINE_FILL_COLOR = 0,
PIPELINE_DRAW_COLOR = 1,
PIPELINE_BLIT = 2,
PIPELINE_COUNT = 3,
NO_PIPELINE = PIPELINE_COUNT
PIPELINE_BLIT = 2,
PIPELINE_COUNT = 3,
NO_PIPELINE = 0x7FFFFFFF
} VKPipeline;
/**
* There are two groups of composite modes:
* - Logic composite - using logicOp.
* - Alpha compisite - using blending.
*/
typedef enum {
LOGIC_COMPOSITE_XOR = 0,
LOGIC_COMPOSITE_GROUP = LOGIC_COMPOSITE_XOR,
ALPHA_COMPOSITE_CLEAR = java_awt_AlphaComposite_CLEAR,
ALPHA_COMPOSITE_SRC = java_awt_AlphaComposite_SRC,
ALPHA_COMPOSITE_DST = java_awt_AlphaComposite_DST,
ALPHA_COMPOSITE_SRC_OVER = java_awt_AlphaComposite_SRC_OVER,
ALPHA_COMPOSITE_DST_OVER = java_awt_AlphaComposite_DST_OVER,
ALPHA_COMPOSITE_SRC_IN = java_awt_AlphaComposite_SRC_IN,
ALPHA_COMPOSITE_DST_IN = java_awt_AlphaComposite_DST_IN,
ALPHA_COMPOSITE_SRC_OUT = java_awt_AlphaComposite_SRC_OUT,
ALPHA_COMPOSITE_DST_OUT = java_awt_AlphaComposite_DST_OUT,
ALPHA_COMPOSITE_SRC_ATOP = java_awt_AlphaComposite_SRC_ATOP,
ALPHA_COMPOSITE_DST_ATOP = java_awt_AlphaComposite_DST_ATOP,
ALPHA_COMPOSITE_XOR = java_awt_AlphaComposite_XOR,
ALPHA_COMPOSITE_GROUP = ALPHA_COMPOSITE_XOR,
COMPOSITE_COUNT = ALPHA_COMPOSITE_GROUP + 1,
NO_COMPOSITE = 0x7FFFFFFF
} VKCompositeMode;
#define COMPOSITE_GROUP(COMPOSITE) ( \
(COMPOSITE) <= LOGIC_COMPOSITE_GROUP ? LOGIC_COMPOSITE_GROUP : \
(COMPOSITE) <= ALPHA_COMPOSITE_GROUP ? ALPHA_COMPOSITE_GROUP : \
NO_COMPOSITE )
extern const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT];
/**
* Global pipeline context.
*/
@@ -51,7 +80,7 @@ struct VKPipelineContext {
VkSampler linearRepeatSampler;
VKShaders* shaders;
struct VKShaders* shaders;
VKRenderPassContext** renderPassContexts;
};
@@ -59,10 +88,10 @@ struct VKPipelineContext {
* Per-format context.
*/
struct VKRenderPassContext {
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass;
VKPipelineSet* pipelineSet; // TODO we will need a real hash map for this in the future.
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass;
struct VKPipelineSet** pipelineSets; // TODO we will need a real hash map for this in the future.
};
typedef struct {
@@ -79,6 +108,6 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);
VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipeline pipeline);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline);
#endif //VKPipelines_h_Included

View File

@@ -94,9 +94,18 @@
// Rendering context is only accessed from VKRenderQueue_flushBuffer,
// which is only called from queue flusher thread, no need for synchronization.
static VKRenderingContext context = {
NULL, {},
{1.0, 0.0, 0.0,0.0, 1.0, 0.0},
{{0, 0}, {INT_MAX, INT_MAX}}};
.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
};
// We keep this color separately from context.color,
// because we need consistent state when switching between XOR and alpha composite modes.
// This variable holds last value set by SET_COLOR, while context.color holds color,
// currently used for drawing, which may have also been provided by SET_XOR_COMPOSITE.
Color color;
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq, jlong buf, jint limit)
@@ -430,11 +439,14 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
break;
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
{
jint rule = NEXT_INT(b);
jint rule = NEXT_INT(b);
jfloat extraAlpha = NEXT_FLOAT(b);
jint flags = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE");
jint flags = NEXT_INT(b);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE(%d, %f, %d)", rule, extraAlpha, flags);
context.color = color;
context.composite = (VKCompositeMode) rule;
context.extraAlpha = extraAlpha;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
@@ -442,12 +454,19 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint xorPixel = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE");
context.color = VKUtil_DecodeJavaColor(xorPixel);
context.color.a = 0.0f; // Alpha is left unchanged in XOR mode.
context.composite = LOGIC_COMPOSITE_XOR;
context.extraAlpha = 1.0f;
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_COMPOSITE");
context.color = color;
context.composite = ALPHA_COMPOSITE_SRC;
context.extraAlpha = 1.0f;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
@@ -582,10 +601,11 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
jint javaColor = NEXT_INT(b);
context.color = VKUtil_DecodeJavaColor(javaColor);
J2dRlsTraceLn5(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_COLOR 0x%08x, linear_rgba={%.3f, %.3f, %.3f, %.3f}",
javaColor, context.color.r, context.color.g, context.color.b, context.color.a);
color = VKUtil_DecodeJavaColor(javaColor);
if (COMPOSITE_GROUP(context.composite) == ALPHA_COMPOSITE_GROUP) context.color = color;
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", javaColor);
J2dTraceLn4(J2D_TRACE_VERBOSE, // Print color values with straight alpha for convenience.
" srgb={%.3f, %.3f, %.3f, %.3f}", color.r/color.a, color.g/color.a, color.b/color.a, color.a);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:

View File

@@ -142,11 +142,12 @@ struct VKRenderPass {
uint32_t vertexCount;
BufferWritingState vertexBufferWriting;
VKPipeline currentPipeline;
VkBool32 pendingFlush;
VkBool32 pendingCommands;
VkBool32 pendingClear;
uint64_t lastTimestamp; // When was this surface last used?
VKCompositeMode currentComposite;
VKPipeline currentPipeline;
VkBool32 pendingFlush;
VkBool32 pendingCommands;
VkBool32 pendingClear;
uint64_t lastTimestamp; // When was this surface last used?
};
/**
@@ -464,6 +465,7 @@ inline void VKRenderer_FlushDraw(VKSDOps* surface) {
*/
static void VKRenderer_ResetDrawing(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL);
surface->renderPass->currentComposite = NO_COMPOSITE;
surface->renderPass->currentPipeline = NO_PIPELINE;
surface->renderPass->firstVertex = 0;
surface->renderPass->vertexCount = 0;
@@ -502,9 +504,7 @@ void VKRenderer_DestroyRenderPass(VKSDOps* surface) {
VKRenderer_Wait(device->renderer, surface->renderPass->lastTimestamp);
VKRenderer_DiscardRenderPass(surface);
// Release resources.
if (surface->renderPass->framebuffer != VK_NULL_HANDLE) {
device->vkDestroyFramebuffer(device->handle, surface->renderPass->framebuffer, NULL);
}
device->vkDestroyFramebuffer(device->handle, surface->renderPass->framebuffer, NULL);
if (surface->renderPass->commandBuffer != VK_NULL_HANDLE) {
POOL_RETURN(device->renderer, secondaryCommandBufferPool, surface->renderPass->commandBuffer);
}
@@ -535,6 +535,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
(*renderPass) = (VKRenderPass) {
.pendingCommands = VK_FALSE,
.pendingClear = VK_TRUE, // Clear the surface by default
.currentComposite = NO_COMPOSITE,
.currentPipeline = NO_PIPELINE,
.lastTimestamp = 0
};
@@ -556,7 +557,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
.layers = 1
};
VK_IF_ERROR(device->vkCreateFramebuffer(device->handle, &framebufferCreateInfo, NULL,
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
@@ -888,28 +889,39 @@ VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
assert(context != NULL && context->surface != NULL);
VKSDOps* surface = context->surface;
// Validate render pass state.
// Init render pass.
if (surface->renderPass == NULL || !surface->renderPass->pendingCommands) {
// We must only [re]init render pass between frames.
// Now this is correct, but in future we may have frames consisting of multiple render passes,
// so we must be careful to NOT call VKRenderer_InitRenderPass between render passes within single frame.
// Be careful to NOT call VKRenderer_InitRenderPass between render passes within single frame.
if (!VKRenderer_InitRenderPass(surface)) return VK_FALSE;
// In the future, we may need to restart the render pass within single frame,
// for example when switching between blended and XOR drawing modes.
// So, generally, this should depend on VKRenderingContext, but now we just start the render pass once.
VKRenderer_BeginRenderPass(surface);
}
VKRenderPass* renderPass = surface->renderPass;
// Validate render pass state.
if (renderPass->currentComposite != context->composite) {
// 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;
// Update state.
VKRenderer_FlushDraw(surface);
renderPass->currentComposite = context->composite;
// Begin render pass.
if (!renderPass->pendingCommands) VKRenderer_BeginRenderPass(surface);
// 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;
}
// Validate current pipeline.
if (renderPass->currentPipeline != pipeline) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
surface->renderPass->currentPipeline, pipeline);
renderPass->currentPipeline, pipeline);
VKRenderer_FlushDraw(surface);
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->currentPipeline = pipeline;
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
VKPipelines_GetPipeline(renderPass->context, pipeline));
VKPipelines_GetPipeline(renderPass->context, context->composite, pipeline));
renderPass->vertexBufferWriting.bound = VK_FALSE;
}
return VK_TRUE;

View File

@@ -31,10 +31,14 @@
#include "VKPipelines.h"
struct VKRenderingContext {
VKSDOps* surface;
Color color;
VKTransform transform;
VkRect2D clipRect;
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;
};
typedef struct {

View File

@@ -84,7 +84,10 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
vksdo->requestedExtent.width != vksdo->image->extent.width ||
vksdo->requestedExtent.height != vksdo->image->extent.height)) {
// VK_FORMAT_B8G8R8A8_UNORM is the most widely-supported format for our use.
// Currently, we only support *_SRGB and *_UNORM formats,
// as other types may not be trivial to alias for logicOp rendering.
VkFormat format = VK_FORMAT_B8G8R8A8_UNORM;
VKImage* image = VKImage_Create(device, vksdo->requestedExtent.width, vksdo->requestedExtent.height,
0, format, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,

View File

@@ -26,7 +26,7 @@
#include <vulkan/vulkan.h>
/**
* Floating-point RGBA color with sRGB encoding.
* Floating-point RGBA color with sRGB encoding and pre-multiplied alpha.
*/
typedef union {
struct {
@@ -56,7 +56,6 @@ typedef struct VKRenderPass VKRenderPass;
typedef struct VKRenderingContext VKRenderingContext;
typedef struct VKPipelineContext VKPipelineContext;
typedef struct VKRenderPassContext VKRenderPassContext;
typedef struct VKShaders VKShaders;
typedef struct VKBuffer VKBuffer;
typedef struct VKImage VKImage;
typedef struct VKSDOps VKSDOps;

View File

@@ -21,9 +21,11 @@
// or visit www.oracle.com if you need additional information or have any
// questions.
#include <assert.h>
#include "VKUtil.h"
Color VKUtil_DecodeJavaColor(uint32_t srgb) {
Color VKUtil_DecodeJavaColor(uint32_t color) {
assert(sizeof(Color) == sizeof(float) * 4);
// Just map [0, 255] integer colors onto [0, 1] floating-point range, it remains in sRGB color space.
// sRGB gamma correction remains unsupported.
static const float NormTable256[256] = {
@@ -32,13 +34,17 @@ Color VKUtil_DecodeJavaColor(uint32_t srgb) {
#define NORM64(N) NORM8(N),NORM8(N+8),NORM8(N+16),NORM8(N+24),NORM8(N+32),NORM8(N+40),NORM8(N+48),NORM8(N+56)
NORM64(0),NORM64(64),NORM64(128),NORM64(192)
};
Color c = {
.r = NormTable256[(srgb >> 16) & 0xFF],
.g = NormTable256[(srgb >> 8) & 0xFF],
.b = NormTable256[ srgb & 0xFF],
.a = NormTable256[(srgb >> 24) & 0xFF]
Color srgb = {
.r = NormTable256[(color >> 16) & 0xFF],
.g = NormTable256[(color >> 8) & 0xFF],
.b = NormTable256[ color & 0xFF],
.a = NormTable256[(color >> 24) & 0xFF]
};
return c;
// Convert to pre-multiplied alpha.
srgb.r *= srgb.a;
srgb.g *= srgb.a;
srgb.b *= srgb.a;
return srgb;
}
uint32_t VKUtil_Log2(uint64_t i) {

View File

@@ -93,10 +93,13 @@ typedef struct {
/**
* Vulkan expects linear colors.
* However Java2D expects legacy behavior, as if colors were blended in sRGB color space.
* Therefore this function just remaps color components from [0, 255] to [0, 1] range,
* they still represent sRGB color.
* Therefore this function converts straight-alpha Java color in range [0, 255]
* to pre-multiplied alpha normalized [0, 1] color, still representing sRGB color.
* This is also accounted for in VKSD_ConfigureWindowSurface, so that Vulkan doesn't do any
* color space conversions on its own, as the colors we are drawing are already in sRGB.
*
* Note: we receive colors from Java with straight (non-premultiplied) alpha, which is done to prevent precision loss.
* This is controlled by PixelConverter parameter of SurfaceType, see VKSurfaceData.java.
*/
Color VKUtil_DecodeJavaColor(uint32_t color);