mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
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:
@@ -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 =
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user