diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index dd638bb7ba..7e9d11137e 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -1604,6 +1604,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; } + if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { image_create_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } @@ -1723,39 +1727,41 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T texture.samples = p_format.samples; texture.allowed_shared_formats = p_format.shareable_formats; - //set bound and unbound layouts + //set base layout based on usage priority + + if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { + //first priority, readable + texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + } else if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) { + //second priority, storage + + texture.layout = VK_IMAGE_LAYOUT_GENERAL; + + } else if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + //third priority, color or depth + + texture.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + } else if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + + texture.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + } else { + texture.layout = VK_IMAGE_LAYOUT_GENERAL; + } + if (p_format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; - texture.barrier_aspect_mask = texture.read_aspect_mask; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (format_has_stencil(p_format.format)) { texture.barrier_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; } - - if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { - texture.unbound_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } else { - texture.unbound_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } - texture.bound_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - } else if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - - texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; - texture.barrier_aspect_mask = texture.read_aspect_mask; - - if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { - texture.unbound_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } else { - texture.unbound_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } - texture.bound_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } else { texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; - texture.barrier_aspect_mask = texture.read_aspect_mask; - - texture.unbound_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - texture.bound_layout = VK_IMAGE_LAYOUT_UNDEFINED; //will never be bound + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; } texture.bound = false; @@ -1825,7 +1831,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_memory_barrier.srcAccessMask = 0; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - image_memory_barrier.newLayout = texture.unbound_layout; + image_memory_barrier.newLayout = texture.layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = texture.image; @@ -1835,7 +1841,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_memory_barrier.subresourceRange.baseArrayLayer = 0; image_memory_barrier.subresourceRange.layerCount = image_create_info.arrayLayers; - vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); } RID id = texture_owner.make_rid(texture); @@ -2081,7 +2087,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con image_memory_barrier.pNext = NULL; image_memory_barrier.srcAccessMask = 0; image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.oldLayout = texture->unbound_layout; + image_memory_barrier.oldLayout = texture->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2221,7 +2227,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - image_memory_barrier.newLayout = texture->unbound_layout; + image_memory_barrier.newLayout = texture->layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = texture->image; @@ -2231,7 +2237,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); } return OK; @@ -2368,7 +2374,7 @@ PoolVector RenderingDeviceVulkan::texture_get_data(RID p_texture, uint3 image_memory_barrier.pNext = NULL; image_memory_barrier.srcAccessMask = 0; image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.oldLayout = tex->unbound_layout; + image_memory_barrier.oldLayout = tex->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2446,7 +2452,7 @@ PoolVector RenderingDeviceVulkan::texture_get_data(RID p_texture, uint3 image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - image_memory_barrier.newLayout = tex->unbound_layout; + image_memory_barrier.newLayout = tex->layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = tex->image; @@ -2456,7 +2462,7 @@ PoolVector RenderingDeviceVulkan::texture_get_data(RID p_texture, uint3 image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); } { //make dst readable @@ -2559,7 +2565,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.pNext = NULL; image_memory_barrier.srcAccessMask = 0; image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - image_memory_barrier.oldLayout = src_tex->unbound_layout; + image_memory_barrier.oldLayout = src_tex->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2579,7 +2585,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.pNext = NULL; image_memory_barrier.srcAccessMask = 0; image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.oldLayout = dst_tex->unbound_layout; + image_memory_barrier.oldLayout = dst_tex->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2631,7 +2637,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - image_memory_barrier.newLayout = src_tex->unbound_layout; + image_memory_barrier.newLayout = src_tex->layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = src_tex->image; @@ -2641,7 +2647,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); } { //make dst readable @@ -2652,7 +2658,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - image_memory_barrier.newLayout = dst_tex->unbound_layout; + image_memory_barrier.newLayout = dst_tex->layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2663,7 +2669,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); } } @@ -2733,7 +2739,8 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector &p_stages uint32_t stages_processed = 0; + bool is_compute = false; + for (int i = 0; i < p_stages.size(); i++) { + if (p_stages[i].shader_stage == SHADER_STAGE_COMPUTE) { + is_compute = true; + ERR_FAIL_COND_V_MSG(p_stages.size() != 1, RID(), + "Compute shaders can only receive one stage, dedicated to compute."); + } ERR_FAIL_COND_V_MSG(stages_processed & (1 << p_stages[i].shader_stage), RID(), "Stage " + String(shader_stage_names[p_stages[i].shader_stage]) + " submitted more than once."); @@ -3793,6 +3807,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector &p_stages shader.vertex_input_mask = vertex_input_mask; shader.fragment_outputs = fragment_outputs; shader.push_constant = push_constant; + shader.is_compute = is_compute; String error_text; @@ -4166,6 +4181,8 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector &p_uniforms, List > image_infos; //used for verification to make sure a uniform set does not use a framebuffer bound texture Vector attachable_textures; + Vector mutable_sampled_textures; + Vector mutable_storage_textures; for (uint32_t i = 0; i < set_uniform_count; i++) { const UniformInfo &set_uniform = set_uniforms[i]; @@ -4259,9 +4276,14 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector &p_uniforms, ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen } - img_info.imageLayout = texture->unbound_layout; + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_info.push_back(img_info); + + if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { + //can also be used as storage, add to mutable sampled + mutable_sampled_textures.push_back(texture); + } } write.dstArrayElement = 0; @@ -4306,9 +4328,14 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector &p_uniforms, ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen } - img_info.imageLayout = texture->unbound_layout; + img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_info.push_back(img_info); + + if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { + //can also be used as storage, add to mutable sampled + mutable_sampled_textures.push_back(texture); + } } write.dstArrayElement = 0; @@ -4321,7 +4348,54 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector &p_uniforms, type_size = uniform.ids.size(); } break; case UNIFORM_TYPE_IMAGE: { - //todo + + if (uniform.ids.size() != set_uniform.length) { + if (set_uniform.length > 1) { + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + } else { + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + } + } + + Vector image_info; + + for (int j = 0; j < uniform.ids.size(); j++) { + Texture *texture = texture_owner.getornull(uniform.ids[j]); + + ERR_FAIL_COND_V_MSG(!texture, RID(), + "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); + + ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), RID(), + "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_STORAGE_BIT usage flag set in order to be used as uniform."); + + VkDescriptorImageInfo img_info; + img_info.sampler = NULL; + img_info.imageView = texture->view; + + if (texture->owner.is_valid()) { + texture = texture_owner.getornull(texture->owner); + ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + } + + img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + image_info.push_back(img_info); + + if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { + //can also be used as storage, add to mutable sampled + mutable_storage_textures.push_back(texture); + } + } + + write.dstArrayElement = 0; + write.descriptorCount = uniform.ids.size(); + write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); + write.pBufferInfo = NULL; + write.pTexelBufferView = NULL; + + type_size = uniform.ids.size(); + } break; case UNIFORM_TYPE_TEXTURE_BUFFER: { if (uniform.ids.size() != set_uniform.length) { @@ -4476,6 +4550,8 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector &p_uniforms, uniform_set.descriptor_set = descriptor_set; uniform_set.format = shader->set_formats[p_shader_set]; uniform_set.attachable_textures = attachable_textures; + uniform_set.mutable_sampled_textures = mutable_sampled_textures; + uniform_set.mutable_storage_textures = mutable_storage_textures; uniform_set.shader_set = p_shader_set; uniform_set.shader_id = p_shader; @@ -4651,6 +4727,9 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma Shader *shader = shader_owner.getornull(p_shader); ERR_FAIL_COND_V(!shader, RID()); + ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), + "Compute shaders can't be used in render pipelines"); + if (p_framebuffer_format == INVALID_ID) { //if nothing provided, use an empty one (no attachments) p_framebuffer_format = framebuffer_format_create(Vector()); @@ -4996,7 +5075,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma pipeline.validation.primitive_minimum = primitive_minimum[p_render_primitive]; #endif //create ID to associate with this pipeline - RID id = pipeline_owner.make_rid(pipeline); + RID id = render_pipeline_owner.make_rid(pipeline); //now add aall the dependencies _add_dependency(id, p_shader); return id; @@ -5004,7 +5083,55 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { _THREAD_SAFE_METHOD_ - return pipeline_owner.owns(p_pipeline); + return render_pipeline_owner.owns(p_pipeline); +} + +/**************************/ +/**** COMPUTE PIPELINE ****/ +/**************************/ + +RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) { + _THREAD_SAFE_METHOD_ + + //needs a shader + Shader *shader = shader_owner.getornull(p_shader); + ERR_FAIL_COND_V(!shader, RID()); + + ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(), + "Non-compute shaders can't be used in compute pipelines"); + + //finally, pipeline create info + VkComputePipelineCreateInfo compute_pipeline_create_info; + + compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + compute_pipeline_create_info.pNext = NULL; + compute_pipeline_create_info.flags = 0; + + compute_pipeline_create_info.stage = shader->pipeline_stages[0]; + compute_pipeline_create_info.layout = shader->pipeline_layout; + compute_pipeline_create_info.basePipelineHandle = NULL; + compute_pipeline_create_info.basePipelineIndex = 0; + + ComputePipeline pipeline; + VkResult err = vkCreateComputePipelines(device, NULL, 1, &compute_pipeline_create_info, NULL, &pipeline.pipeline); + ERR_FAIL_COND_V(err, RID()); + + pipeline.set_formats = shader->set_formats; + pipeline.push_constant_stages = shader->push_constant.push_constants_vk_stage; + pipeline.pipeline_layout = shader->pipeline_layout; + pipeline.shader = p_shader; + pipeline.push_constant_size = shader->push_constant.push_constant_size; + + //create ID to associate with this pipeline + RID id = compute_pipeline_owner.make_rid(pipeline); + //now add aall the dependencies + _add_dependency(id, p_shader); + return id; +} + +bool RenderingDeviceVulkan::compute_pipeline_is_valid(RID p_pipeline) { + + return compute_pipeline_owner.owns(p_pipeline); } /****************/ @@ -5055,6 +5182,8 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(in _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(draw_list != NULL, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != NULL, INVALID_ID, "Only one draw/compute list can be active at the same time."); + VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; draw_list = memnew(DrawList); draw_list->command_buffer = command_buffer; @@ -5256,6 +5385,9 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(draw_list != NULL, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != NULL, INVALID_ID, "Only one draw/compute list can be active at the same time."); + Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer); ERR_FAIL_COND_V(!framebuffer, INVALID_ID); @@ -5530,7 +5662,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - const RenderPipeline *pipeline = pipeline_owner.getornull(p_render_pipeline); + const RenderPipeline *pipeline = render_pipeline_owner.getornull(p_render_pipeline); ERR_FAIL_COND(!pipeline); #ifdef DEBUG_ENABLED ERR_FAIL_COND(pipeline->validation.framebuffer_format != dl->validation.framebuffer_format); @@ -5887,6 +6019,284 @@ void RenderingDeviceVulkan::draw_list_end() { // * Another render pass happens (since we may be done _memory_barrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, true); } + +/***********************/ +/**** COMPUTE LISTS ****/ +/***********************/ + +RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin() { + + ERR_FAIL_COND_V_MSG(draw_list != NULL, INVALID_ID, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != NULL, INVALID_ID, "Only one draw/compute list can be active at the same time."); + + compute_list = memnew(ComputeList); + compute_list->command_buffer = frames[frame].draw_command_buffer; + + return ID_TYPE_COMPUTE_LIST; +} + +void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + + const ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_compute_pipeline); + ERR_FAIL_COND(!pipeline); + + if (p_compute_pipeline == cl->state.pipeline) { + return; //redundant state, return. + } + + cl->state.pipeline = p_compute_pipeline; + cl->state.pipeline_layout = pipeline->pipeline_layout; + + vkCmdBindPipeline(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); + + if (cl->state.pipeline_shader != pipeline->shader) { + // shader changed, so descriptor sets may become incompatible. + + //go through ALL sets, and unbind them (and all those above) if the format is different + + uint32_t pcount = pipeline->set_formats.size(); //formats count in this pipeline + cl->state.set_count = MAX(cl->state.set_count, pcount); + const uint32_t *pformats = pipeline->set_formats.ptr(); //pipeline set formats + + bool sets_valid = true; //once invalid, all above become invalid + for (uint32_t i = 0; i < pcount; i++) { + //if a part of the format is different, invalidate it (and the rest) + if (!sets_valid || cl->state.sets[i].pipeline_expected_format != pformats[i]) { + cl->state.sets[i].bound = false; + cl->state.sets[i].pipeline_expected_format = pformats[i]; + sets_valid = false; + } + } + + for (uint32_t i = pcount; i < cl->state.set_count; i++) { + //unbind the ones above (not used) if exist + cl->state.sets[i].bound = false; + } + + cl->state.set_count = pcount; //update set count + + if (pipeline->push_constant_size) { + cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; +#ifdef DEBUG_ENABLED + cl->validation.pipeline_push_constant_suppplied = false; +#endif + } + } + +#ifdef DEBUG_ENABLED + //update compute pass pipeline info + cl->validation.pipeline_active = true; + cl->validation.pipeline_push_constant_size = pipeline->push_constant_size; +#endif +} +void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_index >= limits.maxBoundDescriptorSets || p_index > MAX_UNIFORM_SETS, + "Attempting to bind a descriptor set (" + itos(p_index) + ") greater than what the hardware supports (" + itos(limits.maxBoundDescriptorSets) + ")."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + + UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set); + ERR_FAIL_COND(!uniform_set); + + if (p_index > cl->state.set_count) { + cl->state.set_count = p_index; + } + + cl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; //update set pointer + cl->state.sets[p_index].bound = false; //needs rebind + cl->state.sets[p_index].uniform_set_format = uniform_set->format; + cl->state.sets[p_index].uniform_set = p_uniform_set; + + uint32_t textures_to_sampled_count = uniform_set->mutable_sampled_textures.size(); + Texture **textures_to_sampled = uniform_set->mutable_sampled_textures.ptrw(); + + for (uint32_t i = 0; i < textures_to_sampled_count; i++) { + if (textures_to_sampled[i]->layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.oldLayout = textures_to_sampled[i]->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = textures_to_sampled[i]->image; + image_memory_barrier.subresourceRange.aspectMask = textures_to_sampled[i]->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = textures_to_sampled[i]->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = textures_to_sampled[i]->layers; + + vkCmdPipelineBarrier(cl->command_buffer, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + + textures_to_sampled[i]->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + cl->state.textures_to_sampled_layout.erase(textures_to_sampled[i]); + } + } + + uint32_t textures_to_storage_count = uniform_set->mutable_storage_textures.size(); + Texture **textures_to_storage = uniform_set->mutable_storage_textures.ptrw(); + + for (uint32_t i = 0; i < textures_to_storage_count; i++) { + if (textures_to_storage[i]->layout != VK_IMAGE_LAYOUT_GENERAL) { + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.oldLayout = textures_to_storage[i]->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = textures_to_storage[i]->image; + image_memory_barrier.subresourceRange.aspectMask = textures_to_sampled[i]->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = textures_to_storage[i]->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = textures_to_storage[i]->layers; + + vkCmdPipelineBarrier(cl->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + + textures_to_storage[i]->layout = VK_IMAGE_LAYOUT_GENERAL; + + cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); //needs to go back to sampled layout afterwards + } + } + +#if 0 + { //validate that textures bound are not attached as framebuffer bindings + uint32_t attachable_count = uniform_set->attachable_textures.size(); + const RID *attachable_ptr = uniform_set->attachable_textures.ptr(); + uint32_t bound_count = draw_list_bound_textures.size(); + const RID *bound_ptr = draw_list_bound_textures.ptr(); + for (uint32_t i = 0; i < attachable_count; i++) { + for (uint32_t j = 0; j < bound_count; j++) { + ERR_FAIL_COND_MSG(attachable_ptr[i] == bound_ptr[j], + "Attempted to use the same texture in framebuffer attachment and a uniform set, this is not allowed."); + } + } + } +#endif +} +void RenderingDeviceVulkan::compute_list_set_push_constant(ComputeListID p_list, void *p_data, uint32_t p_data_size) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(p_data_size != cl->validation.pipeline_push_constant_size, + "This compute pipeline requires (" + itos(cl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); +#endif + vkCmdPushConstants(cl->command_buffer, cl->state.pipeline_layout, cl->state.pipeline_push_constant_stages, 0, p_data_size, p_data); +#ifdef DEBUG_ENABLED + cl->validation.pipeline_push_constant_suppplied = true; +#endif +} +void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) { + ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); + ERR_FAIL_COND(!compute_list); + + ComputeList *cl = compute_list; + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified."); +#endif + +#ifdef DEBUG_ENABLED + + ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); + + if (cl->validation.pipeline_push_constant_size > 0) { + //using push constants, check that they were supplied + ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_suppplied, + "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); + } + +#endif + + //Bind descriptor sets + + for (uint32_t i = 0; i < cl->state.set_count; i++) { + + if (cl->state.sets[i].pipeline_expected_format == 0) { + continue; //nothing expected by this pipeline + } +#ifdef DEBUG_ENABLED + if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { + + if (cl->state.sets[i].uniform_set_format == 0) { + ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline"); + } else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) { + UniformSet *us = uniform_set_owner.getornull(cl->state.sets[i].uniform_set); + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); + } else { + ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader)); + } + } +#endif + if (!cl->state.sets[i].bound) { + //All good, see if this requires re-binding + vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, NULL); + cl->state.sets[i].bound = true; + } + } + + vkCmdDispatch(cl->command_buffer, p_x_groups, p_y_groups, p_z_groups); +} +void RenderingDeviceVulkan::compute_list_end() { + ERR_FAIL_COND(!compute_list); + + for (Set::Element *E = compute_list->state.textures_to_sampled_layout.front(); E; E = E->next()) { + + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = E->get()->layout; + image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = E->get()->image; + image_memory_barrier.subresourceRange.aspectMask = E->get()->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = E->get()->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = E->get()->layers; + + vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); + + E->get()->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + memdelete(compute_list); +} + #if 0 void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_framebuffer, ID *p_draw_lists, uint32_t p_draw_list_count, InitialAction p_initial_action, FinalAction p_final_action, const Vector &p_clear_colors) { @@ -5998,10 +6408,14 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { UniformSet *uniform_set = uniform_set_owner.getornull(p_id); frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set); uniform_set_owner.free(p_id); - } else if (pipeline_owner.owns(p_id)) { - RenderPipeline *pipeline = pipeline_owner.getornull(p_id); - frames[frame].pipelines_to_dispose_of.push_back(*pipeline); - pipeline_owner.free(p_id); + } else if (render_pipeline_owner.owns(p_id)) { + RenderPipeline *pipeline = render_pipeline_owner.getornull(p_id); + frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline); + render_pipeline_owner.free(p_id); + } else if (compute_pipeline_owner.owns(p_id)) { + ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_id); + frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline); + compute_pipeline_owner.free(p_id); } else { ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id())); } @@ -6022,6 +6436,10 @@ void RenderingDeviceVulkan::finalize_frame() { ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work)."); } + if (compute_list) { + ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); + } + { //complete the setup buffer (that needs to be processed before anything else) vkEndCommandBuffer(frames[frame].setup_command_buffer); vkEndCommandBuffer(frames[frame].draw_command_buffer); @@ -6032,12 +6450,20 @@ void RenderingDeviceVulkan::finalize_frame() { void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { //free in dependency usage order, so nothing weird happens //pipelines - while (frames[p_frame].pipelines_to_dispose_of.front()) { - RenderPipeline *pipeline = &frames[p_frame].pipelines_to_dispose_of.front()->get(); + while (frames[p_frame].render_pipelines_to_dispose_of.front()) { + RenderPipeline *pipeline = &frames[p_frame].render_pipelines_to_dispose_of.front()->get(); vkDestroyPipeline(device, pipeline->pipeline, NULL); - frames[p_frame].pipelines_to_dispose_of.pop_front(); + frames[p_frame].render_pipelines_to_dispose_of.pop_front(); + } + + while (frames[p_frame].compute_pipelines_to_dispose_of.front()) { + ComputePipeline *pipeline = &frames[p_frame].compute_pipelines_to_dispose_of.front()->get(); + + vkDestroyPipeline(device, pipeline->pipeline, NULL); + + frames[p_frame].compute_pipelines_to_dispose_of.pop_front(); } //uniform sets @@ -6344,6 +6770,8 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context) { draw_list = NULL; draw_list_count = 0; draw_list_split = false; + + compute_list = NULL; } template @@ -6430,7 +6858,8 @@ void RenderingDeviceVulkan::finalize() { _flush(false); - _free_rids(pipeline_owner, "Pipeline"); + _free_rids(render_pipeline_owner, "Pipeline"); + _free_rids(compute_pipeline_owner, "Compute"); _free_rids(uniform_set_owner, "UniformSet"); _free_rids(texture_buffer_owner, "TextureBuffer"); _free_rids(storage_buffer_owner, "StorageBuffer"); diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index be776bd6e2..2781fc71e2 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -91,6 +91,7 @@ class RenderingDeviceVulkan : public RenderingDevice { ID_TYPE_VERTEX_FORMAT, ID_TYPE_DRAW_LIST, ID_TYPE_SPLIT_DRAW_LIST, + ID_TYPE_COMPUTE_LIST, ID_TYPE_MAX, ID_BASE_SHIFT = 58 //5 bits for ID types }; @@ -138,8 +139,8 @@ class RenderingDeviceVulkan : public RenderingDevice { Vector allowed_shared_formats; - VkImageLayout bound_layout; //layout used when bound to framebuffer being drawn - VkImageLayout unbound_layout; //layout used otherwise + VkImageLayout layout; + uint32_t read_aspect_mask; uint32_t barrier_aspect_mask; bool bound; //bound to framebffer @@ -286,6 +287,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } }; + uint32_t storage_mask; Vector texture_ids; struct Version { @@ -519,6 +521,7 @@ class RenderingDeviceVulkan : public RenderingDevice { PushConstant push_constant; + bool is_compute = false; int max_output; Vector sets; Vector set_formats; @@ -620,6 +623,8 @@ class RenderingDeviceVulkan : public RenderingDevice { VkDescriptorSet descriptor_set; //VkPipelineLayout pipeline_layout; //not owned, inherited from shader Vector attachable_textures; //used for validation + Vector mutable_sampled_textures; //used for layout change + Vector mutable_storage_textures; //used for layout change }; RID_Owner uniform_set_owner; @@ -660,7 +665,19 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t push_constant_stages; }; - RID_Owner pipeline_owner; + RID_Owner render_pipeline_owner; + + struct ComputePipeline { + + RID shader; + Vector set_formats; + VkPipelineLayout pipeline_layout; // not owned, needed for push constants + VkPipeline pipeline; + uint32_t push_constant_size; + uint32_t push_constant_stages; + }; + + RID_Owner compute_pipeline_owner; /*******************/ /**** DRAW LIST ****/ @@ -796,6 +813,74 @@ class RenderingDeviceVulkan : public RenderingDevice { Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_action, FinalAction p_final_action, const Vector &p_clear_colors, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); + /**********************/ + /**** COMPUTE LIST ****/ + /**********************/ + + struct ComputeList { + + VkCommandBuffer command_buffer; //if persistent, this is owned, otherwise it's shared with the ringbuffer + + struct SetState { + uint32_t pipeline_expected_format; + uint32_t uniform_set_format; + VkDescriptorSet descriptor_set; + RID uniform_set; + bool bound; + SetState() { + bound = false; + pipeline_expected_format = 0; + uniform_set_format = 0; + descriptor_set = VK_NULL_HANDLE; + } + }; + + struct State { + Set textures_to_sampled_layout; + + SetState sets[MAX_UNIFORM_SETS]; + uint32_t set_count; + RID pipeline; + RID pipeline_shader; + VkPipelineLayout pipeline_layout; + uint32_t pipeline_push_constant_stages; + + State() { + set_count = 0; + pipeline_layout = VK_NULL_HANDLE; + pipeline_push_constant_stages = 0; + } + } state; +#ifdef DEBUG_ENABLED + + struct Validation { + bool active; //means command buffer was not closes, so you can keep adding things + Vector set_formats; + Vector set_bound; + Vector set_rids; + //last pipeline set values + bool pipeline_active; + RID pipeline_shader; + uint32_t invalid_set_from; + Vector pipeline_set_formats; + uint32_t pipeline_push_constant_size; + bool pipeline_push_constant_suppplied; + + Validation() { + active = true; + invalid_set_from = 0; + + //pipeline state initalize + pipeline_active = false; + pipeline_push_constant_size = 0; + pipeline_push_constant_suppplied = false; + } + } validation; +#endif + }; + + ComputeList *compute_list; + /**************************/ /**** FRAME MANAGEMENT ****/ /**************************/ @@ -823,7 +908,8 @@ class RenderingDeviceVulkan : public RenderingDevice { List shaders_to_dispose_of; List buffer_views_to_dispose_of; List uniform_sets_to_dispose_of; - List pipelines_to_dispose_of; + List render_pipelines_to_dispose_of; + List compute_pipelines_to_dispose_of; VkCommandPool command_pool; VkCommandBuffer setup_command_buffer; //used at the begining of every frame for set-up @@ -940,6 +1026,13 @@ public: virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0); virtual bool render_pipeline_is_valid(RID p_pipeline); + /**************************/ + /**** COMPUTE PIPELINE ****/ + /**************************/ + + virtual RID compute_pipeline_create(RID p_shader); + virtual bool compute_pipeline_is_valid(RID p_pipeline); + /****************/ /**** SCREEN ****/ /****************/ @@ -970,6 +1063,17 @@ public: virtual void draw_list_end(); + /***********************/ + /**** COMPUTE LISTS ****/ + /***********************/ + + virtual ComputeListID compute_list_begin(); + virtual void compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline); + virtual void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index); + virtual void compute_list_set_push_constant(ComputeListID p_list, void *p_data, uint32_t p_data_size); + virtual void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups); + virtual void compute_list_end(); + /**************/ /**** FREE ****/ /**************/ diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 68c6c12a6d..3541b132da 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -170,8 +170,9 @@ void EditorSpatialGizmo::Instance::create_instance(Spatial *p_base, bool p_hidde instance = VS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world()->get_scenario()); VS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id()); - if (skin_reference.is_valid()) + if (skin_reference.is_valid()) { VS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton()); + } if (extra_margin) VS::get_singleton()->instance_set_extra_visibility_margin(instance, 1); VS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, VS::SHADOW_CASTING_SETTING_OFF); diff --git a/gles_builders.py b/gles_builders.py index cde41a8f36..7b2d1ba7d5 100644 --- a/gles_builders.py +++ b/gles_builders.py @@ -509,6 +509,7 @@ class RDHeaderStruct: def __init__(self): self.vertex_lines = [] self.fragment_lines = [] + self.compute_lines = [] self.vertex_included_files = [] self.fragment_included_files = [] @@ -517,6 +518,7 @@ class RDHeaderStruct: self.line_offset = 0 self.vertex_offset = 0 self.fragment_offset = 0 + self.compute_offset = 0 def include_file_in_rd_header(filename, header_data, depth): @@ -539,6 +541,13 @@ def include_file_in_rd_header(filename, header_data, depth): header_data.fragment_offset = header_data.line_offset continue + if line.find("[compute]") != -1: + header_data.reading = "compute" + line = fs.readline() + header_data.line_offset += 1 + header_data.compute_offset = header_data.line_offset + continue + while line.find("#include ") != -1: includeline = line.replace("#include ", "").strip()[1:-1] @@ -553,6 +562,10 @@ def include_file_in_rd_header(filename, header_data, depth): header_data.fragment_included_files += [included_file] if include_file_in_rd_header(included_file, header_data, depth + 1) is None: print("Error in file '" + filename + "': #include " + includeline + "could not be found!") + elif not included_file in header_data.compute_included_files and header_data.reading == "compute": + header_data.compute_included_files += [included_file] + if include_file_in_rd_header(included_file, header_data, depth + 1) is None: + print("Error in file '" + filename + "': #include " + includeline + "could not be found!") line = fs.readline() @@ -563,6 +576,8 @@ def include_file_in_rd_header(filename, header_data, depth): header_data.vertex_lines += [line] if header_data.reading == "fragment": header_data.fragment_lines += [line] + if header_data.reading == "compute": + header_data.compute_lines += [line] line = fs.readline() header_data.line_offset += 1 @@ -572,7 +587,7 @@ def include_file_in_rd_header(filename, header_data, depth): return header_data def build_rd_header(filename): - header_data = LegacyGLHeaderStruct() + header_data = RDHeaderStruct() include_file_in_rd_header(filename, header_data, 0) out_file = filename + ".gen.h" @@ -598,24 +613,39 @@ def build_rd_header(filename): fd.write("\t"+out_file_class+"() {\n\n") + if (len(header_data.compute_lines)): - fd.write("\t\tstatic const char _vertex_code[]={\n") - for x in header_data.vertex_lines: - for c in x: - fd.write(str(ord(c)) + ",") + fd.write("\t\tstatic const char _compute_code[]={\n") + for x in header_data.compute_lines: + for c in x: + fd.write(str(ord(c)) + ",") - fd.write(str(ord('\n')) + ",") - fd.write("\t\t0};\n\n") + fd.write(str(ord('\n')) + ",") - fd.write("\t\tstatic const char _fragment_code[]={\n") - for x in header_data.fragment_lines: - for c in x: - fd.write(str(ord(c)) + ",") + fd.write("\t\t0};\n\n") + fd.write("\t\tsetup(nullptr,nullptr,_compute_code,\""+out_file_class+"\");\n") + fd.write("\t}\n") - fd.write(str(ord('\n')) + ",") - fd.write("\t\t0};\n\n") - fd.write("\t\tsetup(_vertex_code,_fragment_code,\""+out_file_class+"\");\n") - fd.write("\t}\n") + else: + + fd.write("\t\tstatic const char _vertex_code[]={\n") + for x in header_data.vertex_lines: + for c in x: + fd.write(str(ord(c)) + ",") + + fd.write(str(ord('\n')) + ",") + fd.write("\t\t0};\n\n") + + fd.write("\t\tstatic const char _fragment_code[]={\n") + for x in header_data.fragment_lines: + for c in x: + fd.write(str(ord(c)) + ",") + + fd.write(str(ord('\n')) + ",") + + fd.write("\t\t0};\n\n") + fd.write("\t\tsetup(_vertex_code,_fragment_code,nullptr,\""+out_file_class+"\");\n") + fd.write("\t}\n") fd.write("};\n\n") diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 4089e0c23b..3e4cd54664 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -830,7 +830,9 @@ Ref Skeleton::register_skin(const Ref &p_skin) { skin_bindings.insert(skin_ref.operator->()); skin->connect("changed", skin_ref.operator->(), "_skin_changed"); - _make_dirty(); + + _make_dirty(); //skin needs to be updated, so update skeleton + return skin_ref; } diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp index 7bb615eda9..888568e21a 100644 --- a/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp @@ -1469,6 +1469,15 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { sky_ggx_samples_realtime = GLOBAL_GET("rendering/quality/reflections/ggx_samples_realtime"); sky_use_cubemap_array = GLOBAL_GET("rendering/quality/reflections/texture_array_reflections"); // sky_use_cubemap_array = false; + + { + String defines = ""; + Vector versions; + versions.push_back(""); + giprobe_lighting_shader.initialize(versions, defines); + giprobe_lighting_shader_version = giprobe_lighting_shader.version_create(); + giprobe_lighting_shader_version_shader = giprobe_lighting_shader.version_get_shader(giprobe_lighting_shader_version, 0); + } } RasterizerSceneRD::~RasterizerSceneRD() { diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_rd.h b/servers/visual/rasterizer_rd/rasterizer_scene_rd.h index a1897cabe1..97e1d08d90 100644 --- a/servers/visual/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/visual/rasterizer_rd/rasterizer_scene_rd.h @@ -4,6 +4,7 @@ #include "core/rid_owner.h" #include "servers/visual/rasterizer.h" #include "servers/visual/rasterizer_rd/rasterizer_storage_rd.h" +#include "servers/visual/rasterizer_rd/shaders/giprobe_lighting.glsl.gen.h" #include "servers/visual/rendering_device.h" class RasterizerSceneRD : public RasterizerScene { @@ -108,6 +109,12 @@ private: mutable RID_Owner reflection_probe_instance_owner; + /* GIPROBE INSTANCE */ + + GiprobeLightingShaderRD giprobe_lighting_shader; + RID giprobe_lighting_shader_version; + RID giprobe_lighting_shader_version_shader; + /* SHADOW ATLAS */ struct ShadowAtlas { diff --git a/servers/visual/rasterizer_rd/rasterizer_storage_rd.h b/servers/visual/rasterizer_rd/rasterizer_storage_rd.h index 397e3c1b95..6cdc21c2f0 100644 --- a/servers/visual/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/visual/rasterizer_rd/rasterizer_storage_rd.h @@ -778,6 +778,8 @@ public: _FORCE_INLINE_ RID skeleton_get_3d_uniform_set(RID p_skeleton, RID p_shader, uint32_t p_set) const { Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, RID()); + ERR_FAIL_COND_V(skeleton->size == 0, RID()); if (skeleton->use_2d) { return RID(); } diff --git a/servers/visual/rasterizer_rd/shader_rd.cpp b/servers/visual/rasterizer_rd/shader_rd.cpp index 58596f6a72..98e0c99c2e 100644 --- a/servers/visual/rasterizer_rd/shader_rd.cpp +++ b/servers/visual/rasterizer_rd/shader_rd.cpp @@ -33,11 +33,11 @@ #include "rasterizer_rd.h" #include "servers/visual/rendering_device.h" -void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name) { +void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) { name = p_name; //split vertex and shader code (thank you, shader compiler programmers from you know what company). - { + if (p_vertex_code) { String defines_tag = "\nVERSION_DEFINES"; String globals_tag = "\nVERTEX_SHADER_GLOBALS"; String material_tag = "\nMATERIAL_UNIFORMS"; @@ -79,7 +79,7 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con } } - { + if (p_fragment_code) { String defines_tag = "\nVERSION_DEFINES"; String globals_tag = "\nFRAGMENT_SHADER_GLOBALS"; String material_tag = "\nMATERIAL_UNIFORMS"; @@ -135,6 +135,50 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con } } } + + if (p_compute_code) { + is_compute = true; + + String defines_tag = "\nVERSION_DEFINES"; + String globals_tag = "\nCOMPUTE_SHADER_GLOBALS"; + String material_tag = "\nMATERIAL_UNIFORMS"; + String code_tag = "\nCOMPUTE_SHADER_CODE"; + String code = p_compute_code; + + int cpos = code.find(defines_tag); + if (cpos != -1) { + compute_codev = code.substr(0, cpos).ascii(); + code = code.substr(cpos + defines_tag.length(), code.length()); + } + + cpos = code.find(material_tag); + + if (cpos == -1) { + compute_code0 = code.ascii(); + } else { + compute_code0 = code.substr(0, cpos).ascii(); + code = code.substr(cpos + material_tag.length(), code.length()); + + cpos = code.find(globals_tag); + + if (cpos == -1) { + compute_code1 = code.ascii(); + } else { + + compute_code1 = code.substr(0, cpos).ascii(); + String code2 = code.substr(cpos + globals_tag.length(), code.length()); + + cpos = code2.find(code_tag); + if (cpos == -1) { + compute_code2 = code2.ascii(); + } else { + + compute_code2 = code2.substr(0, cpos).ascii(); + compute_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii(); + } + } + } + } } RID ShaderRD::version_create() { @@ -171,7 +215,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { RD::ShaderStage current_stage = RD::SHADER_STAGE_VERTEX; bool build_ok = true; - { + if (!is_compute) { //vertex stage StringBuilder builder; @@ -211,7 +255,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { } } - if (build_ok) { + if (!is_compute && build_ok) { //fragment stage current_stage = RD::SHADER_STAGE_FRAGMENT; @@ -256,9 +300,50 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { } } + if (is_compute) { + //compute stage + current_stage = RD::SHADER_STAGE_COMPUTE; + + StringBuilder builder; + + builder.append(compute_codev.get_data()); // version info (if exists) + builder.append("\n"); //make sure defines begin at newline + builder.append(general_defines.get_data()); + builder.append(variant_defines[p_variant].get_data()); + + for (int j = 0; j < p_version->custom_defines.size(); j++) { + builder.append(p_version->custom_defines[j].get_data()); + } + + builder.append(compute_code0.get_data()); //first part of compute + + builder.append(p_version->uniforms.get_data()); //uniforms (same for compute and fragment) + + builder.append(compute_code1.get_data()); //second part of compute + + builder.append(p_version->compute_globals.get_data()); // compute globals + + builder.append(compute_code2.get_data()); //third part of compute + + builder.append(p_version->compute_code.get_data()); // code + + builder.append(compute_code3.get_data()); //fourth of compute + + current_source = builder.as_string(); + RD::ShaderStageData stage; + stage.spir_v = RD::get_singleton()->shader_compile_from_source(RD::SHADER_STAGE_COMPUTE, current_source, RD::SHADER_LANGUAGE_GLSL, &error); + if (stage.spir_v.size() == 0) { + build_ok = false; + } else { + + stage.shader_stage = RD::SHADER_STAGE_COMPUTE; + stages.push_back(stage); + } + } + if (!build_ok) { variant_set_mutex.lock(); //properly print the errors - ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment") + " shader, variant #" + itos(p_variant) + " (" + variant_defines[p_variant].get_data() + ")."); + ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(p_variant) + " (" + variant_defines[p_variant].get_data() + ")."); ERR_PRINT(error); #ifdef DEBUG_ENABLED @@ -319,6 +404,8 @@ void ShaderRD::_compile_version(Version *p_version) { void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector &p_custom_defines) { + ERR_FAIL_COND(is_compute); + Version *version = version_owner.getornull(p_version); ERR_FAIL_COND(!version); version->vertex_globals = p_vertex_globals.utf8(); @@ -340,6 +427,28 @@ void ShaderRD::version_set_code(RID p_version, const String &p_uniforms, const S } } +void ShaderRD::version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector &p_custom_defines) { + + ERR_FAIL_COND(!is_compute); + + Version *version = version_owner.getornull(p_version); + ERR_FAIL_COND(!version); + version->compute_globals = p_compute_globals.utf8(); + version->compute_code = p_compute_code.utf8(); + version->uniforms = p_uniforms.utf8(); + + version->custom_defines.clear(); + for (int i = 0; i < p_custom_defines.size(); i++) { + version->custom_defines.push_back(p_custom_defines[i].utf8()); + } + + version->dirty = true; + if (version->initialize_needed) { + _compile_version(version); + version->initialize_needed = false; + } +} + bool ShaderRD::version_is_valid(RID p_version) { Version *version = version_owner.getornull(p_version); ERR_FAIL_COND_V(!version, false); diff --git a/servers/visual/rasterizer_rd/shader_rd.h b/servers/visual/rasterizer_rd/shader_rd.h index 81169343d6..4d1e9576ce 100644 --- a/servers/visual/rasterizer_rd/shader_rd.h +++ b/servers/visual/rasterizer_rd/shader_rd.h @@ -55,6 +55,8 @@ class ShaderRD { CharString uniforms; CharString vertex_globals; CharString vertex_code; + CharString compute_globals; + CharString compute_code; CharString fragment_light; CharString fragment_globals; CharString fragment_code; @@ -89,16 +91,25 @@ class ShaderRD { CharString vertex_code2; CharString vertex_code3; + bool is_compute = false; + + CharString compute_codev; //for version and extensions + CharString compute_code0; + CharString compute_code1; + CharString compute_code2; + CharString compute_code3; + const char *name; protected: ShaderRD() {} - void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name); + void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name); public: RID version_create(); void version_set_code(RID p_version, const String &p_uniforms, const String &p_vertex_globals, const String &p_vertex_code, const String &p_fragment_globals, const String &p_fragment_light, const String &p_fragment_code, const Vector &p_custom_defines); + void version_set_compute_code(RID p_version, const String &p_uniforms, const String &p_compute_globals, const String &p_compute_code, const Vector &p_custom_defines); _FORCE_INLINE_ RID version_get_shader(RID p_version, int p_variant) { ERR_FAIL_INDEX_V(p_variant, variant_defines.size(), RID()); diff --git a/servers/visual/rasterizer_rd/shaders/SCsub b/servers/visual/rasterizer_rd/shaders/SCsub index b1fa9cd3f4..660523e29f 100644 --- a/servers/visual/rasterizer_rd/shaders/SCsub +++ b/servers/visual/rasterizer_rd/shaders/SCsub @@ -11,4 +11,5 @@ if 'RD_GLSL' in env['BUILDERS']: env.RD_GLSL('sky.glsl'); env.RD_GLSL('tonemap.glsl'); env.RD_GLSL('copy.glsl'); + env.RD_GLSL('giprobe_lighting.glsl'); diff --git a/servers/visual/rasterizer_rd/shaders/giprobe_lighting.glsl b/servers/visual/rasterizer_rd/shaders/giprobe_lighting.glsl new file mode 100644 index 0000000000..cec25f86f9 --- /dev/null +++ b/servers/visual/rasterizer_rd/shaders/giprobe_lighting.glsl @@ -0,0 +1,241 @@ +[compute] + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +#define NO_CHILDREN 0xFFFFFFFF +#define GREY_VEC vec3(0.33333,0.33333,0.33333) + +struct CellPosition { + uint children[8]; +}; + + +layout(set=0,binding=1,std140) buffer CellPositions { + CellPosition data[]; +} cell_positions; + +struct CellMaterial { + uint position; // xyz 10 bits + uint albedo; //rgb albedo + uint emission; //rgb normalized with e as multiplier + uint normal; //RGB normal encoded +}; + +layout(set=0,binding=2,std140) buffer CellMaterials { + CellMaterial data[]; +} cell_materials; + +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_OMNI 1 +#define LIGHT_TYPE_SPOT 2 + +struct Light { + + uint type; + float energy; + float radius; + float attenuation; + + vec3 color; + float spot_angle_radians; + + float advance; + float max_length; + uint pad0; + uint pad2; + + vec3 position; + float spot_attenuation; + + + vec3 direction; + bool visible; + + vec4 clip_planes[3]; +}; + +layout(set=0,binding=3,std140) buffer Lights { + Light data[]; +} lights; + + +layout(set=0,binding=4,std140) uniform Params { + vec3 limits; + float max_length; + uint size; + uint stack_size; + uint light_count; + float emission_scale; +} params; + + +layout (rgba8,set=0,binding=5) uniform restrict writeonly image3D color_tex; + + +uint raymarch(float distance,float distance_adv,vec3 from,vec3 direction) { + + uint result = NO_CHILDREN; + + while (distance > -distance_adv) { //use this to avoid precision errors + + uint cell = 0; + + ivec3 pos = ivec3(from); + ivec3 ofs = ivec3(0); + ivec3 half_size = ivec3(params.size) / 2; + if (any(lessThan(pos,ivec3(0))) || any(greaterThanEqual(pos,ivec3(params.size)))) { + return NO_CHILDREN; //outside range + } + + for (int i = 0; i < params.stack_size - 1; i++) { + + bvec3 greater = greaterThanEqual(pos,ofs+half_size); + + ofs += mix(ivec3(0),half_size,greater); + + uint child = 0; //wonder if this can be done faster + if (greater.x) { + child|=1; + } + if (greater.y) { + child|=2; + } + if (greater.z) { + child|=4; + } + + cell = cell_positions.data[cell].children[child]; + if (cell == NO_CHILDREN) + break; + + half_size >>= ivec3(1); + } + + if ( cell != NO_CHILDREN) { + return cell; //found cell! + } + + from += direction * distance_adv; + distance -= distance_adv; + } + + return NO_CHILDREN; +} + +bool compute_light_vector(uint light,uint cell, vec3 pos,out float attenuation, out vec3 light_pos) { + + if (lights.data[light].type==LIGHT_TYPE_DIRECTIONAL) { + + light_pos = pos - lights.data[light].direction * params.max_length; + attenuation = 1.0; + + } else { + + light_pos = lights.data[light].position; + float distance = length(pos - light_pos); + if (distance >= lights.data[light].radius) { + return false; + } + + attenuation = pow( distance / lights.data[light].radius + 0.0001, lights.data[light].attenuation ); + + + if (lights.data[light].type==LIGHT_TYPE_SPOT) { + + vec3 rel = normalize(pos - light_pos); + float angle = acos(dot(rel,lights.data[light].direction)); + if (angle > lights.data[light].spot_angle_radians) { + return false; + } + + float d = clamp(angle / lights.data[light].spot_angle_radians, 0, 1); + attenuation *= pow(1.0 - d, lights.data[light].spot_attenuation); + } + } + + return true; +} + +void main() { + + uint cell_index = gl_GlobalInvocationID.x; + + uvec3 posu = uvec3(cell_materials.data[cell_index].position&0x3FF,(cell_materials.data[cell_index].position>>10)&0x3FF,cell_materials.data[cell_index].position>>20); + vec3 pos = vec3(posu); + + vec3 emission = vec3(ivec3(cell_materials.data[cell_index].emission&0x3FF,(cell_materials.data[cell_index].emission>>10)&0x7FF,cell_materials.data[cell_index].emission>>21)) * params.emission_scale; + vec4 albedo = unpackUnorm4x8(cell_materials.data[cell_index].albedo); + vec4 normal = unpackSnorm4x8(cell_materials.data[cell_index].normal); //w >0.5 means, all directions + +#ifdef MODE_ANISOTROPIC + vec3 accum[6]=vec3[](vec3(0.0),vec3(0.0),vec3(0.0),vec3(0.0),vec3(0.0),vec3(0.0)); + const vec3 accum_dirs[6]=vec3[](vec3(1.0,0.0,0.0),vec3(-1.0,0.0,0.0),vec3(0.0,1.0,0.0),vec3(0.0,-1.0,0.0),vec3(0.0,0.0,1.0),vec3(0.0,0.0,-1.0)); +#else + vec3 accum = vec3(0); +#endif + + for(uint i=0;i=0) { + continue; //not facing the light + } + + uint result = raymarch(distance,distance_adv,from,lights.data[i].direction); + + if (result != cell_index) { + continue; //was occluded + } + + vec3 light = lights.data[i].color * albedo.rgb * attenuation; + +#ifdef MODE_ANISOTROPIC + for(uint j=0;j<6;j++) { + accum[j]+=max(0.0,dot(accum_dir,-light_dir))*light+emission; + } +#else + if (normal.w < 0.5) { + accum+=max(0.0,dot(normal.xyz,-light_dir))*light+emission; + } else { + //all directions + accum+=light+emission; + } +#endif + + } + +#ifdef MODE_ANISOTROPIC + + vec3 accum_total = accum[0]+accum[1]+accum[2]+accum[3]+accum[4]+accum[5]; + float accum_total_energy = max(dot(accum_total,GREY_VEC),0.00001); + vec3 iso_positive = vec3(dot(aniso[0],GREY_VEC),dot(aniso[2],GREY_VEC),dot(aniso[4],GREY_VEC))/vec3(accum_total_energy); + vec3 iso_negative = vec3(dot(aniso[1],GREY_VEC),dot(aniso[3],GREY_VEC),dot(aniso[5],GREY_VEC))/vec3(accum_total_energy); + + //store in 3D textures, total color, and isotropic magnitudes +#else + //store in 3D texture pos, accum + imageStore(color_tex,ivec3(posu),vec4(accum,albedo.a)); +#endif + +} diff --git a/servers/visual/rendering_device.h b/servers/visual/rendering_device.h index 859a9e798c..d0afb3f13b 100644 --- a/servers/visual/rendering_device.h +++ b/servers/visual/rendering_device.h @@ -882,6 +882,13 @@ public: virtual RID render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags = 0) = 0; virtual bool render_pipeline_is_valid(RID p_pipeline) = 0; + /**************************/ + /**** COMPUTE PIPELINE ****/ + /**************************/ + + virtual RID compute_pipeline_create(RID p_shader) = 0; + virtual bool compute_pipeline_is_valid(RID p_pipeline) = 0; + /****************/ /**** SCREEN ****/ /****************/ @@ -930,6 +937,19 @@ public: virtual void draw_list_end() = 0; + /***********************/ + /**** COMPUTE LISTS ****/ + /***********************/ + + typedef int64_t ComputeListID; + + virtual ComputeListID compute_list_begin() = 0; + virtual void compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) = 0; + virtual void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) = 0; + virtual void compute_list_set_push_constant(ComputeListID p_list, void *p_data, uint32_t p_data_size) = 0; + virtual void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) = 0; + virtual void compute_list_end() = 0; + /***************/ /**** FREE! ****/ /***************/