Files
JetBrainsRuntime/src/java.desktop/share/native/common/java2d/vulkan/VKPipelines.c
2025-12-06 01:03:49 +01:00

568 lines
27 KiB
C

// Copyright 2024 JetBrains s.r.o.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License version 2 only, as
// published by the Free Software Foundation. Oracle designates this
// particular file as subject to the "Classpath" exception as provided
// by Oracle in the LICENSE file that accompanied this code.
//
// This code is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// version 2 for more details (a copy is included in the LICENSE file that
// accompanied this code).
//
// You should have received a copy of the GNU General Public License version
// 2 along with this work; if not, write to the Free Software Foundation,
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
// or visit www.oracle.com if you need additional information or have any
// questions.
#include <assert.h>
#include "VKUtil.h"
#include "VKEnv.h"
#include "VKPipelines.h"
#define INCLUDE_BYTECODE
#define SHADER_ENTRY(NAME, TYPE) static uint32_t NAME ## _ ## TYPE ## _data[] = {
#define BYTECODE_END };
#include "vulkan/shader_list.h"
#undef INCLUDE_BYTECODE
#undef SHADER_ENTRY
#undef BYTECODE_END
static void hash(uint32_t* result, int i) { // Good for hashing enums.
uint32_t x = (uint32_t) i;
x = ((x >> 16U) ^ x) * 0x45d9f3bU;
x = ((x >> 16U) ^ x) * 0x45d9f3bU;
x = (x >> 16U) ^ x;
*result ^= x + 0x9e3779b9U + (*result << 6U) + (*result >> 2U);
}
static size_t pipelineDescriptorHash(const void* ptr) {
const VKPipelineDescriptor* d = ptr;
uint32_t h = 0U;
hash(&h, d->stencilMode);
hash(&h, d->dstOpaque);
hash(&h, d->inAlphaType);
hash(&h, d->composite);
hash(&h, d->shader);
hash(&h, d->shaderVariant);
hash(&h, d->topology);
return (size_t) h;
}
static bool pipelineDescriptorEquals(const void* ap, const void* bp) {
const VKPipelineDescriptor *a = ap, *b = bp;
return a->stencilMode == b->stencilMode &&
a->dstOpaque == b->dstOpaque &&
a->inAlphaType == b->inAlphaType &&
a->composite == b->composite &&
a->shader == b->shader &&
a->shaderVariant == b->shaderVariant &&
a->topology == b->topology;
}
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);
if (shaders == NULL) return;
# define SHADER_ENTRY(NAME, TYPE) if (shaders->NAME##_##TYPE.module != VK_NULL_HANDLE) \
device->vkDestroyShaderModule(device->handle, shaders->NAME##_##TYPE.module, NULL);
# include "vulkan/shader_list.h"
# undef SHADER_ENTRY
free(shaders);
}
static VKShaders* VKPipelines_CreateShaders(VKDevice* device) {
assert(device != NULL);
const VkShaderStageFlagBits vert = VK_SHADER_STAGE_VERTEX_BIT;
const VkShaderStageFlagBits frag = VK_SHADER_STAGE_FRAGMENT_BIT;
VKShaders* shaders = (VKShaders*) calloc(1, sizeof(VKShaders));
VK_RUNTIME_ASSERT(shaders);
VkShaderModuleCreateInfo createInfo = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
# define SHADER_ENTRY(NAME, TYPE) \
shaders->NAME ## _ ## TYPE = (VkPipelineShaderStageCreateInfo) { \
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, \
.stage = (TYPE), \
.pName = "main" \
}; \
createInfo.codeSize = sizeof(NAME ## _ ## TYPE ## _data); \
createInfo.pCode = NAME ## _ ## TYPE ## _data; \
VK_IF_ERROR(device->vkCreateShaderModule(device->handle, \
&createInfo, NULL, &shaders->NAME##_##TYPE.module)) { \
VKPipelines_DestroyShaders(device, shaders); \
return NULL; \
}
# include "vulkan/shader_list.h"
# undef SHADER_ENTRY
return shaders;
}
#define MAKE_INPUT_STATE(NAME, TYPE, ...) \
const VkFormat INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[] = { __VA_ARGS__ }; \
VkVertexInputAttributeDescription \
INPUT_STATE_ATTRIBUTES_##NAME[SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTE_FORMATS_##NAME)]; \
const VkVertexInputBindingDescription INPUT_STATE_BINDING_##NAME = { \
.binding = 0, \
.stride = sizeof(TYPE), \
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
}; \
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 \
}; \
uint32_t INPUT_STATE_BINDING_SIZE_##NAME = 0; \
for (uint32_t i = 0; i < SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME); i++) { \
INPUT_STATE_ATTRIBUTES_##NAME[i] = (VkVertexInputAttributeDescription) { \
i, 0, INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[i], INPUT_STATE_BINDING_SIZE_##NAME}; \
INPUT_STATE_BINDING_SIZE_##NAME += \
VKUtil_GetFormatGroup(INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[i]).bytes; \
} if (sizeof(TYPE) != INPUT_STATE_BINDING_SIZE_##NAME) VK_FATAL_ERROR("Vertex size mismatch for input state " #NAME)
static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPassContext, uint32_t count,
const VKPipelineDescriptor* descriptors) {
assert(renderPassContext != NULL && renderPassContext->pipelineContext != NULL);
assert(count > 0 && descriptors != NULL);
VKPipelineContext* pipelineContext = renderPassContext->pipelineContext;
VKDevice* device = pipelineContext->device;
VKShaders* shaders = pipelineContext->shaders;
VKComposites* composites = &VKEnv_GetInstance()->composites;
VKPipelineInfo pipelineInfos[count];
// Setup pipeline creation structs.
static const uint32_t MAX_DYNAMIC_STATES = 2;
typedef struct {
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
} ShaderStages;
ShaderStages stages[count];
typedef struct {
uint32_t inAlphaType, outAlphaType, shaderVariant, shaderModifier;
} SpecializationData;
const VkSpecializationMapEntry SPECIALIZATION_ENTRIES[] = {
{ 0, 0, 4 },
{ 1, 4, 4 },
{ 2, 8, 4 },
{ 3, 12, 4 }
};
typedef struct {
VkSpecializationInfo info;
SpecializationData data;
} Specialization;
Specialization specializations[count];
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStates[count];
VkPipelineDepthStencilStateCreateInfo depthStencilStates[count];
VkPipelineDynamicStateCreateInfo dynamicStates[count];
VkDynamicState dynamicStateValues[count][MAX_DYNAMIC_STATES];
VkGraphicsPipelineCreateInfo createInfos[count];
for (uint32_t i = 0; i < count; i++) {
const VKCompositeState* compositeState =
VKComposites_GetState(composites, descriptors[i].composite, descriptors[i].dstOpaque);
pipelineInfos[i].outAlphaType = compositeState->outAlphaType;
// Init default pipeline state. Some members are left uninitialized:
// - pStages (but stageCount is set to 2)
// - pVertexInputState
// - createInfo.layout
specializations[i] = (Specialization) {
.info = {
.mapEntryCount = SARRAY_COUNT_OF(SPECIALIZATION_ENTRIES),
.pMapEntries = SPECIALIZATION_ENTRIES,
.dataSize = sizeof(SpecializationData),
.pData = &specializations[i].data
},
.data = {
descriptors[i].inAlphaType, pipelineInfos[i].outAlphaType, (uint32_t) descriptors[i].shaderVariant,
(descriptors[i].composite == LOGIC_COMPOSITE_XOR ? 1 : 0) |
(descriptors[i].shader & SHADER_MASK ? 2 : 0)
}
};
inputAssemblyStates[i] = (VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = descriptors[i].topology
};
static const VkViewport viewport = {};
static const VkRect2D scissor = {};
static const VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scissor
};
static const VkPipelineRasterizationStateCreateInfo rasterizationState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.lineWidth = 1.0f
};
static const VkPipelineMultisampleStateCreateInfo multisampleState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
static 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
};
depthStencilStates[i] = (VkPipelineDepthStencilStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.stencilTestEnable = descriptors[i].stencilMode == STENCIL_MODE_ON,
.front = stencilOpState,
.back = stencilOpState
};
dynamicStates[i] = (VkPipelineDynamicStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
.pDynamicStates = dynamicStateValues[i]
};
dynamicStateValues[i][0] = VK_DYNAMIC_STATE_VIEWPORT;
dynamicStateValues[i][1] = VK_DYNAMIC_STATE_SCISSOR;
createInfos[i] = (VkGraphicsPipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = stages[i].createInfos,
.pInputAssemblyState = &inputAssemblyStates[i],
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationState,
.pMultisampleState = &multisampleState,
.pDepthStencilState = &depthStencilStates[i],
.pColorBlendState = &compositeState->blendState,
.pDynamicState = &dynamicStates[i],
.renderPass = renderPassContext->renderPass[descriptors[i].stencilMode != STENCIL_MODE_NONE],
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1
};
}
// Setup input states.
MAKE_INPUT_STATE(PRIMITIVE, VKVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(MASK_FILL, VKMaskFillVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(BLIT, VKTxVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT);
MAKE_INPUT_STATE(CLIP, VKIntVertex, VK_FORMAT_R32G32_SINT);
for (uint32_t i = 0; i < count; i++) {
// Setup shader-specific pipeline parameters.
switch ((int) descriptors[i].shader) {
case SHADER_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->commonPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
break;
case SHADER_COLOR | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_color_vert, shaders->mask_fill_color_frag }};
break;
case SHADER_GRADIENT:
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->primitive_vert, shaders->gradient_frag }};
break;
case SHADER_GRADIENT | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_vert, shaders->gradient_frag }};
break;
case SHADER_BLIT:
createInfos[i].pVertexInputState = &INPUT_STATE_BLIT;
createInfos[i].layout = pipelineContext->texturePipelineLayout;
stages[i] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
break;
case SHADER_CLIP:
createInfos[i].pVertexInputState = &INPUT_STATE_CLIP;
static 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
};
static 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
};
createInfos[i].pDepthStencilState = &CLIP_STENCIL_STATE;
createInfos[i].layout = pipelineContext->texturePipelineLayout;
createInfos[i].stageCount = 1;
stages[i] = (ShaderStages) {{ shaders->clip_vert }};
break;
default:
VK_FATAL_ERROR("Cannot create pipeline, unknown shader requested!");
}
for (uint32_t j = 0; j < createInfos[i].stageCount; j++) {
stages[i].createInfos[j].pSpecializationInfo = &specializations[i].info;
}
assert(createInfos[i].pDynamicState->dynamicStateCount <= MAX_DYNAMIC_STATES);
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: stencilMode=%d, dstOpaque=%d, composite=%d, shader=%d, topology=%d",
descriptors[i].stencilMode, descriptors[i].dstOpaque, descriptors[i].composite, descriptors[i].shader, descriptors[i].topology);
}
// Create pipelines.
// TODO pipeline cache
VkPipeline pipelines[count];
VK_IF_ERROR(device->vkCreateGraphicsPipelines(device->handle, VK_NULL_HANDLE, count,
createInfos, NULL, pipelines)) VK_UNHANDLED_ERROR();
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: created %d pipelines", count);
for (uint32_t i = 0; i < count; i++) {
pipelineInfos[i].pipeline = pipelines[i];
pipelineInfos[i].layout = createInfos[i].layout;
MAP_AT(renderPassContext->pipelines, descriptors[i]) = pipelineInfos[i];
}
return pipelineInfos[0];
}
static VkResult VKPipelines_InitRenderPasses(VKDevice* device, VKRenderPassContext* renderPassContext) {
assert(device != NULL && renderPassContext != NULL);
VkAttachmentDescription attachments[] = {{
.format = renderPassContext->format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.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
};
VkRenderPassCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = attachments,
.subpassCount = 1,
.pSubpasses = &subpassDescription,
.dependencyCount = 0,
.pDependencies = NULL
};
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) {
if (renderPassContext == NULL) return;
VKDevice* device = renderPassContext->pipelineContext->device;
assert(device != NULL);
for (const VKPipelineDescriptor* k = NULL; (k = MAP_NEXT_KEY(renderPassContext->pipelines, k)) != NULL;) {
const VKPipelineInfo* info = MAP_FIND(renderPassContext->pipelines, *k);
device->vkDestroyPipeline(device->handle, info->pipeline, NULL);
}
MAP_FREE(renderPassContext->pipelines);
for (uint32_t i = 0; i < 2; i++) {
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass[i], NULL);
}
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_DestroyRenderPassContext(%p): format=%d",
renderPassContext, renderPassContext->format);
free(renderPassContext);
}
static VKRenderPassContext* VKPipelines_CreateRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format) {
assert(pipelineContext != NULL && pipelineContext->device != NULL);
VKRenderPassContext* renderPassContext = calloc(1, sizeof(VKRenderPassContext));
VK_RUNTIME_ASSERT(renderPassContext);
HASH_MAP_REHASH(renderPassContext->pipelines, linear_probing,
&pipelineDescriptorEquals, &pipelineDescriptorHash, 0, 10, 0.75);
renderPassContext->pipelineContext = pipelineContext;
renderPassContext->format = format;
VK_IF_ERROR(VKPipelines_InitRenderPasses(pipelineContext->device, renderPassContext)) {
VKPipelines_DestroyRenderPassContext(renderPassContext);
return NULL;
}
// TODO create few common pipelines in advance? Like default shaders for SRC_OVER composite.
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreateRenderPassContext(%p): format=%d", renderPassContext, format);
return renderPassContext;
}
static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineContext* pipelines) {
assert(device != NULL && pipelines != NULL);
VkResult result;
// We want all our pipelines to have the same push constant ranges to ensure a common state is compatible between pipelines.
VkPushConstantRange pushConstantRanges[] = {{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(VKTransform)
}, {
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = PUSH_CONSTANTS_OFFSET,
.size = PUSH_CONSTANTS_SIZE
}};
// Common pipeline.
VkPipelineLayoutCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pushConstantRangeCount = SARRAY_COUNT_OF(pushConstantRanges),
.pPushConstantRanges = pushConstantRanges
};
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->commonPipelineLayout);
VK_IF_ERROR(result) return result;
// Mask fill pipeline.
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
// Texture pipeline.
VkDescriptorSetLayoutBinding textureLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo textureDescriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &textureLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &textureDescriptorSetLayoutCreateInfo, NULL, &pipelines->textureDescriptorSetLayout);
VK_IF_ERROR(result) return result;
VkDescriptorSetLayout textureDescriptorSetLayouts[] = {
pipelines->textureDescriptorSetLayout,
pipelines->samplers.descriptorSetLayout
};
createInfo.setLayoutCount = SARRAY_COUNT_OF(textureDescriptorSetLayouts);
createInfo.pSetLayouts = textureDescriptorSetLayouts;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
VK_IF_ERROR(result) return result;
return VK_SUCCESS;
}
VKPipelineContext* VKPipelines_CreateContext(VKDevice* device) {
assert(device != NULL);
VKPipelineContext* pipelineContext = (VKPipelineContext*) calloc(1, sizeof(VKPipelineContext));
VK_RUNTIME_ASSERT(pipelineContext);
pipelineContext->device = device;
pipelineContext->samplers = VKSamplers_Create(device);
if (pipelineContext->samplers.descriptorPool == VK_NULL_HANDLE) {
VKPipelines_DestroyContext(pipelineContext);
return NULL;
}
pipelineContext->shaders = VKPipelines_CreateShaders(device);
if (pipelineContext->shaders == NULL) {
VKPipelines_DestroyContext(pipelineContext);
return NULL;
}
VK_IF_ERROR(VKPipelines_InitPipelineLayouts(device, pipelineContext)) {
VKPipelines_DestroyContext(pipelineContext);
return NULL;
}
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreateContext(%p)", pipelineContext);
return pipelineContext;
}
void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
if (pipelineContext == NULL) return;
VKDevice* device = pipelineContext->device;
assert(device != NULL);
for (uint32_t i = 0; i < pipelineContext->renderPassContexts.size; i++) {
VKPipelines_DestroyRenderPassContext(pipelineContext->renderPassContexts.data[i]);
}
ARRAY_FREE(pipelineContext->renderPassContexts);
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->commonPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->maskFillDescriptorSetLayout, NULL);
VKSamplers_Destroy(device, pipelineContext->samplers);
J2dRlsTraceLn(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 < pipelineContext->renderPassContexts.size; i++) {
if (pipelineContext->renderPassContexts.data[i]->format == format) {
return pipelineContext->renderPassContexts.data[i];
}
}
// Not found, create.
VKRenderPassContext* renderPassContext = VKPipelines_CreateRenderPassContext(pipelineContext, format);
ARRAY_PUSH_BACK(pipelineContext->renderPassContexts) = renderPassContext;
return renderPassContext;
}
VKPipelineInfo VKPipelines_GetPipelineInfo(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor) {
assert(renderPassContext != NULL);
VKPipelineInfo info = MAP_AT(renderPassContext->pipelines, descriptor);
if (info.pipeline == VK_NULL_HANDLE) {
info = VKPipelines_CreatePipelines(renderPassContext, 1, &descriptor);
}
return info;
}