Add Chromium-only Blender WebEngine parity work
This commit is contained in:
996
blender-5.2.0/source/blender/gpu/CMakeLists.txt
Normal file
996
blender-5.2.0/source/blender/gpu/CMakeLists.txt
Normal file
@@ -0,0 +1,996 @@
|
||||
# SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# WebEngine is a data/modeling module. Rendering is implemented by the local
|
||||
# Three.js adapter, so expose Blender GPU headers but do not compile a second
|
||||
# renderer into WebAssembly.
|
||||
if(WITH_WEB)
|
||||
add_library(bf_gpu INTERFACE)
|
||||
target_include_directories(bf_gpu INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/intern
|
||||
)
|
||||
add_library(bf::gpu ALIAS bf_gpu)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
dummy
|
||||
intern
|
||||
metal
|
||||
opengl
|
||||
vulkan
|
||||
../makesrna
|
||||
|
||||
# For theme color access.
|
||||
../editors/include
|
||||
|
||||
# For `*_info.hh` includes.
|
||||
../compositor/shaders/infos
|
||||
../draw/engines/eevee
|
||||
../draw/engines/eevee/shaders/infos
|
||||
../draw/engines/gpencil
|
||||
../draw/engines/gpencil/shaders/infos
|
||||
../draw/engines/image/shaders/infos
|
||||
../draw/engines/overlay/shaders/infos
|
||||
../draw/engines/select
|
||||
../draw/engines/select/shaders/infos
|
||||
../draw/engines/workbench
|
||||
../draw/engines/workbench/shaders/infos
|
||||
../draw/intern
|
||||
../draw/intern/shaders
|
||||
metal/kernels
|
||||
vulkan/shaders
|
||||
shaders/infos
|
||||
|
||||
|
||||
# For shader includes
|
||||
shaders/common
|
||||
shaders
|
||||
|
||||
../../../intern/ghost
|
||||
../../../intern/mantaflow/extern
|
||||
../../../intern/opensubdiv
|
||||
)
|
||||
|
||||
if(WITH_BUILDINFO)
|
||||
add_definitions(-DWITH_BUILDINFO)
|
||||
endif()
|
||||
|
||||
if(WITH_RENDERDOC)
|
||||
list(APPEND INC
|
||||
../../../extern/renderdoc/include
|
||||
../../../intern/renderdoc_dynload/include
|
||||
)
|
||||
add_definitions(-DWITH_RENDERDOC)
|
||||
endif()
|
||||
|
||||
if(WITH_GPU_SHADER_ASSERT)
|
||||
add_definitions(-DWITH_GPU_SHADER_ASSERT)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
endif()
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/gpu_batch.cc
|
||||
intern/gpu_batch_presets.cc
|
||||
intern/gpu_batch_utils.cc
|
||||
intern/gpu_capabilities.cc
|
||||
intern/gpu_codegen.cc
|
||||
intern/gpu_compute.cc
|
||||
intern/gpu_context.cc
|
||||
intern/gpu_debug.cc
|
||||
intern/gpu_framebuffer.cc
|
||||
intern/gpu_immediate.cc
|
||||
intern/gpu_immediate_util.cc
|
||||
intern/gpu_index_buffer.cc
|
||||
intern/gpu_init_exit.cc
|
||||
intern/gpu_material.cc
|
||||
intern/gpu_matrix.cc
|
||||
intern/gpu_node_graph.cc
|
||||
intern/gpu_pass.cc
|
||||
intern/gpu_platform.cc
|
||||
intern/gpu_query.cc
|
||||
intern/gpu_select.cc
|
||||
intern/gpu_select_next.cc
|
||||
intern/gpu_select_pick.cc
|
||||
intern/gpu_select_sample_query.cc
|
||||
intern/gpu_shader.cc
|
||||
intern/gpu_shader_builtin.cc
|
||||
intern/gpu_shader_create_info.cc
|
||||
intern/gpu_shader_dependency.cc
|
||||
intern/gpu_shader_interface.cc
|
||||
intern/gpu_shader_log.cc
|
||||
intern/gpu_shader_preprocessor.cc
|
||||
intern/gpu_state.cc
|
||||
intern/gpu_storage_buffer.cc
|
||||
intern/gpu_texture.cc
|
||||
intern/gpu_texture_mipmap.cc
|
||||
intern/gpu_texture_pool.cc
|
||||
intern/gpu_uniform_buffer.cc
|
||||
intern/gpu_vertex_buffer.cc
|
||||
intern/gpu_vertex_format.cc
|
||||
intern/gpu_vertex_format_normals.cc
|
||||
intern/gpu_viewport.cc
|
||||
intern/gpu_worker.cc
|
||||
|
||||
shader_tool/attribute.cc
|
||||
shader_tool/enum.cc
|
||||
shader_tool/flow_control.cc
|
||||
shader_tool/function.cc
|
||||
shader_tool/grammar.cc
|
||||
shader_tool/intermediate.cc
|
||||
shader_tool/metadata.cc
|
||||
shader_tool/namespace.cc
|
||||
shader_tool/processor.cc
|
||||
shader_tool/resource_table.cc
|
||||
shader_tool/string.cc
|
||||
shader_tool/struct.cc
|
||||
shader_tool/template.cc
|
||||
shader_tool/union.cc
|
||||
|
||||
shader_tool/lexit/lexit.cc
|
||||
|
||||
GPU_attribute_convert.hh
|
||||
GPU_batch.hh
|
||||
GPU_batch_presets.hh
|
||||
GPU_batch_utils.hh
|
||||
GPU_capabilities.hh
|
||||
GPU_common.hh
|
||||
GPU_common_types.hh
|
||||
GPU_compilation_subprocess.hh
|
||||
GPU_compute.hh
|
||||
GPU_context.hh
|
||||
GPU_debug.hh
|
||||
GPU_format.hh
|
||||
GPU_framebuffer.hh
|
||||
GPU_immediate.hh
|
||||
GPU_immediate_util.hh
|
||||
GPU_index_buffer.hh
|
||||
GPU_init_exit.hh
|
||||
GPU_material.hh
|
||||
GPU_matrix.hh
|
||||
GPU_pass.hh
|
||||
GPU_platform.hh
|
||||
GPU_platform_backend_enum.h
|
||||
GPU_primitive.hh
|
||||
GPU_select.hh
|
||||
GPU_shader.hh
|
||||
GPU_shader_builtin.hh
|
||||
GPU_shader_shared.hh
|
||||
GPU_state.hh
|
||||
GPU_storage_buffer.hh
|
||||
GPU_texture.hh
|
||||
GPU_texture_pool.hh
|
||||
GPU_uniform_buffer.hh
|
||||
GPU_vertex_buffer.hh
|
||||
GPU_vertex_format.hh
|
||||
GPU_viewport.hh
|
||||
GPU_worker.hh
|
||||
|
||||
intern/gpu_backend.hh
|
||||
intern/gpu_capabilities_private.hh
|
||||
intern/gpu_codegen.hh
|
||||
intern/gpu_context_private.hh
|
||||
intern/gpu_debug_private.hh
|
||||
intern/gpu_framebuffer_private.hh
|
||||
intern/gpu_immediate_private.hh
|
||||
intern/gpu_material_library.hh
|
||||
intern/gpu_matrix_private.hh
|
||||
intern/gpu_node_graph.hh
|
||||
intern/gpu_platform_private.hh
|
||||
intern/gpu_private.hh
|
||||
intern/gpu_profile_report.hh
|
||||
intern/gpu_query.hh
|
||||
intern/gpu_select_private.hh
|
||||
intern/gpu_shader_create_info.hh
|
||||
intern/gpu_shader_create_info_pipeline.hh
|
||||
intern/gpu_shader_create_info_private.hh
|
||||
intern/gpu_shader_dead_code_elimination.hh
|
||||
intern/gpu_shader_dependency_private.hh
|
||||
intern/gpu_shader_interface.hh
|
||||
intern/gpu_shader_metadata_private.hh
|
||||
intern/gpu_shader_private.hh
|
||||
intern/gpu_state_private.hh
|
||||
intern/gpu_storage_buffer_private.hh
|
||||
intern/gpu_texture_pool_private.hh
|
||||
intern/gpu_texture_private.hh
|
||||
intern/gpu_uniform_buffer_private.hh
|
||||
intern/gpu_vertex_format_private.hh
|
||||
|
||||
dummy/dummy_backend.hh
|
||||
dummy/dummy_batch.hh
|
||||
dummy/dummy_context.hh
|
||||
dummy/dummy_framebuffer.hh
|
||||
dummy/dummy_vertex_buffer.hh
|
||||
)
|
||||
|
||||
set(OPENGL_SRC
|
||||
|
||||
opengl/gl_backend.cc
|
||||
opengl/gl_batch.cc
|
||||
opengl/gl_compilation_subprocess.cc
|
||||
opengl/gl_compute.cc
|
||||
opengl/gl_context.cc
|
||||
opengl/gl_debug.cc
|
||||
opengl/gl_framebuffer.cc
|
||||
opengl/gl_immediate.cc
|
||||
opengl/gl_index_buffer.cc
|
||||
opengl/gl_query.cc
|
||||
opengl/gl_shader.cc
|
||||
opengl/gl_shader_interface.cc
|
||||
opengl/gl_shader_log.cc
|
||||
opengl/gl_state.cc
|
||||
opengl/gl_storage_buffer.cc
|
||||
opengl/gl_texture.cc
|
||||
opengl/gl_texture_pool.cc
|
||||
opengl/gl_uniform_buffer.cc
|
||||
opengl/gl_vertex_array.cc
|
||||
opengl/gl_vertex_buffer.cc
|
||||
|
||||
opengl/gl_backend.hh
|
||||
opengl/gl_batch.hh
|
||||
opengl/gl_compilation_subprocess.hh
|
||||
opengl/gl_compute.hh
|
||||
opengl/gl_context.hh
|
||||
opengl/gl_debug.hh
|
||||
opengl/gl_framebuffer.hh
|
||||
opengl/gl_immediate.hh
|
||||
opengl/gl_index_buffer.hh
|
||||
opengl/gl_primitive.hh
|
||||
opengl/gl_query.hh
|
||||
opengl/gl_shader.hh
|
||||
opengl/gl_shader_interface.hh
|
||||
opengl/gl_state.hh
|
||||
opengl/gl_storage_buffer.hh
|
||||
opengl/gl_texture.hh
|
||||
opengl/gl_texture_pool.hh
|
||||
opengl/gl_uniform_buffer.hh
|
||||
opengl/gl_vertex_array.hh
|
||||
opengl/gl_vertex_buffer.hh
|
||||
)
|
||||
|
||||
set(VULKAN_SRC
|
||||
vulkan/vk_backend.cc
|
||||
vulkan/vk_batch.cc
|
||||
vulkan/vk_buffer.cc
|
||||
vulkan/vk_buffer_pool.cc
|
||||
vulkan/vk_common.cc
|
||||
vulkan/vk_context.cc
|
||||
vulkan/vk_data_conversion.cc
|
||||
vulkan/vk_debug.cc
|
||||
vulkan/vk_descriptor_pools.cc
|
||||
vulkan/vk_descriptor_set.cc
|
||||
vulkan/vk_descriptor_set_layouts.cc
|
||||
vulkan/vk_device.cc
|
||||
vulkan/vk_device_submission.cc
|
||||
vulkan/vk_fence.cc
|
||||
vulkan/vk_framebuffer.cc
|
||||
vulkan/vk_image_view.cc
|
||||
vulkan/vk_immediate.cc
|
||||
vulkan/vk_index_buffer.cc
|
||||
vulkan/vk_memory_layout.cc
|
||||
vulkan/vk_memory_pool.cc
|
||||
vulkan/vk_pipeline_pool.cc
|
||||
vulkan/vk_pixel_buffer.cc
|
||||
vulkan/vk_push_constants.cc
|
||||
vulkan/vk_query.cc
|
||||
vulkan/render_graph/nodes/vk_pipeline_data.cc
|
||||
vulkan/render_graph/vk_command_buffer_wrapper.cc
|
||||
vulkan/render_graph/vk_command_builder.cc
|
||||
vulkan/render_graph/vk_render_graph.cc
|
||||
vulkan/render_graph/vk_render_graph_links.cc
|
||||
vulkan/render_graph/vk_resource_access_info.cc
|
||||
vulkan/render_graph/vk_resource_state_tracker.cc
|
||||
vulkan/render_graph/vk_scheduler.cc
|
||||
vulkan/vk_resource_pool.cc
|
||||
vulkan/vk_sampler.cc
|
||||
vulkan/vk_samplers.cc
|
||||
vulkan/vk_shader.cc
|
||||
vulkan/vk_shader_compiler.cc
|
||||
vulkan/vk_shader_interface.cc
|
||||
vulkan/vk_shader_log.cc
|
||||
vulkan/vk_shader_module.cc
|
||||
vulkan/vk_staging_buffer.cc
|
||||
vulkan/vk_state_manager.cc
|
||||
vulkan/vk_storage_buffer.cc
|
||||
vulkan/vk_streaming_buffer.cc
|
||||
vulkan/vk_texture.cc
|
||||
vulkan/vk_texture_pool.cc
|
||||
vulkan/vk_to_string.cc
|
||||
vulkan/vk_uniform_buffer.cc
|
||||
vulkan/vk_vertex_attribute_object.cc
|
||||
vulkan/vk_vertex_buffer.cc
|
||||
|
||||
vulkan/vk_backend.hh
|
||||
vulkan/vk_batch.hh
|
||||
vulkan/vk_buffer.hh
|
||||
vulkan/vk_buffer_pool.hh
|
||||
vulkan/vk_common.hh
|
||||
vulkan/vk_context.hh
|
||||
vulkan/vk_data_conversion.hh
|
||||
vulkan/vk_debug.hh
|
||||
vulkan/vk_descriptor_pools.hh
|
||||
vulkan/vk_descriptor_set.hh
|
||||
vulkan/vk_descriptor_set_layouts.hh
|
||||
vulkan/vk_device.hh
|
||||
vulkan/vk_fence.hh
|
||||
vulkan/vk_framebuffer.hh
|
||||
vulkan/vk_ghost_api.hh
|
||||
vulkan/vk_graphics_pipeline.hh
|
||||
vulkan/vk_image_view.hh
|
||||
vulkan/vk_immediate.hh
|
||||
vulkan/vk_index_buffer.hh
|
||||
vulkan/vk_memory.hh
|
||||
vulkan/vk_memory_layout.hh
|
||||
vulkan/vk_memory_pool.hh
|
||||
vulkan/vk_pipeline_pool.hh
|
||||
vulkan/vk_pixel_buffer.hh
|
||||
vulkan/vk_push_constants.hh
|
||||
vulkan/vk_query.hh
|
||||
vulkan/render_graph/nodes/vk_begin_query_node.hh
|
||||
vulkan/render_graph/nodes/vk_begin_rendering_node.hh
|
||||
vulkan/render_graph/nodes/vk_blit_image_node.hh
|
||||
vulkan/render_graph/nodes/vk_clear_attachments_node.hh
|
||||
vulkan/render_graph/nodes/vk_clear_color_image_node.hh
|
||||
vulkan/render_graph/nodes/vk_clear_depth_stencil_image_node.hh
|
||||
vulkan/render_graph/nodes/vk_copy_buffer_node.hh
|
||||
vulkan/render_graph/nodes/vk_copy_buffer_to_image_node.hh
|
||||
vulkan/render_graph/nodes/vk_copy_image_node.hh
|
||||
vulkan/render_graph/nodes/vk_copy_image_to_buffer_node.hh
|
||||
vulkan/render_graph/nodes/vk_dispatch_indirect_node.hh
|
||||
vulkan/render_graph/nodes/vk_dispatch_node.hh
|
||||
vulkan/render_graph/nodes/vk_draw_indexed_indirect_node.hh
|
||||
vulkan/render_graph/nodes/vk_draw_indexed_node.hh
|
||||
vulkan/render_graph/nodes/vk_draw_indirect_node.hh
|
||||
vulkan/render_graph/nodes/vk_draw_node.hh
|
||||
vulkan/render_graph/nodes/vk_end_query_node.hh
|
||||
vulkan/render_graph/nodes/vk_end_rendering_node.hh
|
||||
vulkan/render_graph/nodes/vk_fill_buffer_node.hh
|
||||
vulkan/render_graph/nodes/vk_node_info.hh
|
||||
vulkan/render_graph/nodes/vk_pipeline_data.hh
|
||||
vulkan/render_graph/nodes/vk_reset_query_pool_node.hh
|
||||
vulkan/render_graph/nodes/vk_synchronization_node.hh
|
||||
vulkan/render_graph/nodes/vk_update_buffer_node.hh
|
||||
vulkan/render_graph/nodes/vk_update_mipmaps_node.hh
|
||||
vulkan/render_graph/vk_command_buffer_wrapper.hh
|
||||
vulkan/render_graph/vk_command_builder.hh
|
||||
vulkan/render_graph/vk_render_graph.hh
|
||||
vulkan/render_graph/vk_render_graph_links.hh
|
||||
vulkan/render_graph/vk_render_graph_node.hh
|
||||
vulkan/render_graph/vk_resource_access_info.hh
|
||||
vulkan/render_graph/vk_resource_state_tracker.hh
|
||||
vulkan/render_graph/vk_scheduler.hh
|
||||
vulkan/vk_resource_pool.hh
|
||||
vulkan/vk_sampler.hh
|
||||
vulkan/vk_samplers.hh
|
||||
vulkan/vk_shader.hh
|
||||
vulkan/vk_shader_compiler.hh
|
||||
vulkan/vk_shader_interface.hh
|
||||
vulkan/vk_shader_log.hh
|
||||
vulkan/vk_shader_module.hh
|
||||
vulkan/vk_staging_buffer.hh
|
||||
vulkan/vk_state_manager.hh
|
||||
vulkan/vk_storage_buffer.hh
|
||||
vulkan/vk_streaming_buffer.hh
|
||||
vulkan/vk_texture.hh
|
||||
vulkan/vk_texture_pool.hh
|
||||
vulkan/vk_to_string.hh
|
||||
vulkan/vk_uniform_buffer.hh
|
||||
vulkan/vk_vertex_attribute_object.hh
|
||||
vulkan/vk_vertex_buffer.hh
|
||||
vulkan/vk_vertex_input_description.hh
|
||||
)
|
||||
|
||||
set(METAL_SRC
|
||||
metal/mtl_backend.mm
|
||||
metal/mtl_batch.mm
|
||||
metal/mtl_command_buffer.mm
|
||||
metal/mtl_context.mm
|
||||
metal/mtl_debug.mm
|
||||
metal/mtl_framebuffer.mm
|
||||
metal/mtl_immediate.mm
|
||||
metal/mtl_index_buffer.mm
|
||||
metal/mtl_memory.mm
|
||||
metal/mtl_push_constant.cc
|
||||
metal/mtl_query.mm
|
||||
metal/mtl_shader.mm
|
||||
metal/mtl_shader_generate.cc
|
||||
metal/mtl_shader_interface.mm
|
||||
metal/mtl_shader_log.mm
|
||||
metal/mtl_state.mm
|
||||
metal/mtl_storage_buffer.mm
|
||||
metal/mtl_texture.mm
|
||||
metal/mtl_texture_pool.mm
|
||||
metal/mtl_texture_util.mm
|
||||
metal/mtl_uniform_buffer.mm
|
||||
metal/mtl_vertex_buffer.mm
|
||||
|
||||
metal/mtl_backend.hh
|
||||
metal/mtl_batch.hh
|
||||
metal/mtl_capabilities.hh
|
||||
metal/mtl_command_buffer.hh
|
||||
metal/mtl_common.hh
|
||||
metal/mtl_context.hh
|
||||
metal/mtl_debug.hh
|
||||
metal/mtl_framebuffer.hh
|
||||
metal/mtl_immediate.hh
|
||||
metal/mtl_index_buffer.hh
|
||||
metal/mtl_memory.hh
|
||||
metal/mtl_primitive.hh
|
||||
metal/mtl_pso_descriptor_state.hh
|
||||
metal/mtl_push_constant.hh
|
||||
metal/mtl_query.hh
|
||||
metal/mtl_shader.hh
|
||||
metal/mtl_shader_generate.hh
|
||||
metal/mtl_shader_interface.hh
|
||||
metal/mtl_shader_interface_type.hh
|
||||
metal/mtl_shader_log.hh
|
||||
metal/mtl_shader_shared.hh
|
||||
metal/mtl_state.hh
|
||||
metal/mtl_storage_buffer.hh
|
||||
metal/mtl_texture.hh
|
||||
metal/mtl_texture_pool.hh
|
||||
metal/mtl_uniform_buffer.hh
|
||||
metal/mtl_vertex_buffer.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::draw
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::atomic
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::dependencies::opencolorio
|
||||
)
|
||||
|
||||
# Select Backend source based on availability
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
list(APPEND SRC
|
||||
${OPENGL_SRC}
|
||||
)
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::dependencies::epoxy
|
||||
)
|
||||
add_definitions(-DWITH_OPENGL_BACKEND)
|
||||
endif()
|
||||
|
||||
if(WITH_METAL_BACKEND)
|
||||
list(APPEND SRC ${METAL_SRC})
|
||||
endif()
|
||||
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
list(APPEND SRC
|
||||
${VULKAN_SRC}
|
||||
)
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::extern::vulkan_memory_allocator
|
||||
PRIVATE bf::extern::xxhash
|
||||
PRIVATE bf::dependencies::optional::vulkan
|
||||
PRIVATE bf::dependencies::optional::shaderc
|
||||
)
|
||||
|
||||
add_definitions(-DWITH_VULKAN_BACKEND)
|
||||
endif()
|
||||
|
||||
set(GLSL_SRC
|
||||
GPU_shader_shared.hh
|
||||
|
||||
shaders/infos/gpu_clip_planes_infos.hh
|
||||
shaders/infos/gpu_index_load_infos.hh
|
||||
shaders/infos/gpu_interface_infos.hh
|
||||
shaders/infos/gpu_shader_2D_area_borders_infos.hh
|
||||
shaders/infos/gpu_shader_2D_checker_infos.hh
|
||||
shaders/infos/gpu_shader_2D_diag_stripes_infos.hh
|
||||
shaders/infos/gpu_shader_2D_image_desaturate_color_infos.hh
|
||||
shaders/infos/gpu_shader_2D_image_infos.hh
|
||||
shaders/infos/gpu_shader_2D_image_overlays_merge_infos.hh
|
||||
shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_infos.hh
|
||||
shaders/infos/gpu_shader_2D_image_rect_color_infos.hh
|
||||
shaders/infos/gpu_shader_2D_image_shuffle_color_infos.hh
|
||||
shaders/infos/gpu_shader_2D_node_socket_infos.hh
|
||||
shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_infos.hh
|
||||
shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_infos.hh
|
||||
shaders/infos/gpu_shader_2D_point_varying_size_varying_color_infos.hh
|
||||
shaders/infos/gpu_shader_2D_widget_infos.hh
|
||||
shaders/infos/gpu_shader_3D_depth_only_infos.hh
|
||||
shaders/infos/gpu_shader_3D_flat_color_infos.hh
|
||||
shaders/infos/gpu_shader_3D_image_infos.hh
|
||||
shaders/infos/gpu_shader_3D_point_infos.hh
|
||||
shaders/infos/gpu_shader_3D_polyline_infos.hh
|
||||
shaders/infos/gpu_shader_3D_smooth_color_infos.hh
|
||||
shaders/infos/gpu_shader_3D_uniform_color_infos.hh
|
||||
shaders/infos/gpu_shader_fullscreen_infos.hh
|
||||
shaders/infos/gpu_shader_gpencil_stroke_infos.hh
|
||||
shaders/infos/gpu_shader_icon_infos.hh
|
||||
shaders/infos/gpu_shader_index_infos.hh
|
||||
shaders/infos/gpu_shader_keyframe_shape_infos.hh
|
||||
shaders/infos/gpu_shader_line_dashed_uniform_color_infos.hh
|
||||
shaders/infos/gpu_shader_print_infos.hh
|
||||
shaders/infos/gpu_shader_sequencer_infos.hh
|
||||
shaders/infos/gpu_shader_simple_lighting_infos.hh
|
||||
shaders/infos/gpu_shader_text_infos.hh
|
||||
shaders/infos/gpu_srgb_to_framebuffer_space_infos.hh
|
||||
|
||||
shaders/gpu_shader_depth_only_frag.glsl
|
||||
shaders/gpu_shader_uniform_color_frag.glsl
|
||||
shaders/gpu_shader_checker_frag.glsl
|
||||
shaders/gpu_shader_diag_stripes_frag.glsl
|
||||
shaders/gpu_shader_simple_lighting_frag.glsl
|
||||
shaders/gpu_shader_flat_color_frag.glsl
|
||||
shaders/gpu_shader_2D_vert.glsl
|
||||
shaders/gpu_shader_2D_area_borders_vert.glsl
|
||||
shaders/gpu_shader_2D_area_borders_frag.glsl
|
||||
shaders/gpu_shader_2D_widget_base.bsl.hh
|
||||
shaders/gpu_shader_2D_widget_shadow_vert.glsl
|
||||
shaders/gpu_shader_2D_widget_shadow_frag.glsl
|
||||
shaders/gpu_shader_2D_node_socket_frag.glsl
|
||||
shaders/gpu_shader_2D_node_socket_vert.glsl
|
||||
shaders/gpu_shader_2D_nodelink.bsl.hh
|
||||
shaders/gpu_shader_2D_line_dashed_frag.glsl
|
||||
shaders/gpu_shader_2D_image_vert.glsl
|
||||
shaders/gpu_shader_2D_image_rect_vert.glsl
|
||||
shaders/gpu_shader_icon_multi_vert.glsl
|
||||
shaders/gpu_shader_icon_frag.glsl
|
||||
shaders/gpu_shader_icon_vert.glsl
|
||||
shaders/gpu_shader_image_frag.glsl
|
||||
shaders/gpu_shader_image_desaturate_frag.glsl
|
||||
shaders/gpu_shader_image_overlays_merge_frag.glsl
|
||||
shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl
|
||||
shaders/gpu_shader_image_shuffle_color_frag.glsl
|
||||
shaders/gpu_shader_image_color_frag.glsl
|
||||
shaders/gpu_shader_3D_image_vert.glsl
|
||||
shaders/gpu_shader_3D_vert.glsl
|
||||
shaders/gpu_shader_3D_normal_vert.glsl
|
||||
shaders/gpu_shader_3D_flat_color_vert.glsl
|
||||
shaders/gpu_shader_3D_line_dashed_uniform_color_vert.glsl
|
||||
shaders/gpu_shader_3D_polyline_frag.glsl
|
||||
shaders/gpu_shader_3D_polyline_vert.glsl
|
||||
shaders/gpu_shader_3D_smooth_color_vert.glsl
|
||||
shaders/gpu_shader_3D_smooth_color_frag.glsl
|
||||
shaders/gpu_shader_3D_clipped_uniform_color_vert.glsl
|
||||
shaders/gpu_shader_2D_update_mipmaps.bsl.hh
|
||||
|
||||
shaders/gpu_shader_point_uniform_color_aa_frag.glsl
|
||||
shaders/gpu_shader_point_uniform_color_outline_aa_frag.glsl
|
||||
shaders/gpu_shader_point_varying_color_frag.glsl
|
||||
shaders/gpu_shader_3D_point_varying_size_varying_color_vert.glsl
|
||||
shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl
|
||||
shaders/gpu_shader_3D_point_flat_color_vert.glsl
|
||||
shaders/gpu_shader_2D_point_varying_size_varying_color_vert.glsl
|
||||
shaders/gpu_shader_2D_point_uniform_size_aa_vert.glsl
|
||||
shaders/gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl
|
||||
|
||||
shaders/gpu_shader_text_vert.glsl
|
||||
shaders/gpu_shader_text_frag.glsl
|
||||
shaders/gpu_shader_keyframe_shape_vert.glsl
|
||||
shaders/gpu_shader_keyframe_shape_frag.glsl
|
||||
|
||||
shaders/gpu_shader_sequencer_scope_comp.glsl
|
||||
shaders/gpu_shader_sequencer_scope_frag.glsl
|
||||
shaders/gpu_shader_sequencer_strips_vert.glsl
|
||||
shaders/gpu_shader_sequencer_strips_frag.glsl
|
||||
shaders/gpu_shader_sequencer_thumbs_vert.glsl
|
||||
shaders/gpu_shader_sequencer_thumbs_frag.glsl
|
||||
shaders/gpu_shader_sequencer_zebra_frag.glsl
|
||||
|
||||
shaders/gpu_shader_codegen_lib.glsl
|
||||
|
||||
shaders/gpu_shader_python_comp.glsl
|
||||
shaders/gpu_shader_python_frag.glsl
|
||||
shaders/gpu_shader_python_typedef_lib.glsl
|
||||
shaders/gpu_shader_python_vert.glsl
|
||||
|
||||
shaders/common/gpu_shader_attribute_load_lib.glsl
|
||||
shaders/common/gpu_shader_bicubic_sampler_lib.glsl
|
||||
shaders/common/gpu_shader_common_color_ramp.glsl
|
||||
shaders/common/gpu_shader_common_color_utils.glsl
|
||||
shaders/common/gpu_shader_common_curves.glsl
|
||||
shaders/common/gpu_shader_common_hash.glsl
|
||||
shaders/common/gpu_shader_common_math_utils.glsl
|
||||
shaders/common/gpu_shader_common_math.glsl
|
||||
shaders/common/gpu_shader_common_mix_rgb.glsl
|
||||
shaders/common/gpu_shader_debug_gradients_lib.glsl
|
||||
shaders/common/gpu_shader_fullscreen_vert.glsl
|
||||
shaders/common/gpu_shader_fullscreen_lib.glsl
|
||||
shaders/common/gpu_shader_index_load_lib.glsl
|
||||
shaders/common/gpu_shader_index_range_lib.glsl
|
||||
shaders/common/gpu_shader_math_angle_lib.glsl
|
||||
shaders/common/gpu_shader_math_axis_angle_lib.glsl
|
||||
shaders/common/gpu_shader_math_base_lib.glsl
|
||||
shaders/common/gpu_shader_math_constants_lib.glsl
|
||||
shaders/common/gpu_shader_math_euler_lib.glsl
|
||||
shaders/common/gpu_shader_math_fast_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_adjoint_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_compare_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_construct_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_conversion_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_interpolate_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_normalize_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_projection_lib.glsl
|
||||
shaders/common/gpu_shader_math_matrix_transform_lib.glsl
|
||||
shaders/common/gpu_shader_math_quaternion_lib.glsl
|
||||
shaders/common/gpu_shader_math_rotation_conversion_lib.glsl
|
||||
shaders/common/gpu_shader_math_rotation_lib.glsl
|
||||
shaders/common/gpu_shader_math_safe_lib.glsl
|
||||
shaders/common/gpu_shader_math_vector_compare_lib.glsl
|
||||
shaders/common/gpu_shader_math_vector_lib.glsl
|
||||
shaders/common/gpu_shader_math_vector_reduce_lib.glsl
|
||||
shaders/common/gpu_shader_math_vector_safe_lib.glsl
|
||||
shaders/common/gpu_shader_offset_indices_lib.glsl
|
||||
shaders/common/gpu_shader_print_lib.glsl
|
||||
shaders/common/gpu_shader_ray_utils_lib.glsl
|
||||
shaders/common/gpu_shader_ray_lib.glsl
|
||||
shaders/common/gpu_shader_sequencer_lib.glsl
|
||||
shaders/common/gpu_shader_shared_exponent_lib.glsl
|
||||
shaders/common/gpu_shader_smaa_lib.glsl
|
||||
shaders/common/gpu_shader_test_lib.bsl.hh
|
||||
shaders/common/gpu_shader_tiled_image_lookup_lib.glsl
|
||||
shaders/common/gpu_shader_utildefines_lib.glsl
|
||||
|
||||
shaders/material/gpu_shader_material_add_shader.glsl
|
||||
shaders/material/gpu_shader_material_align_rotation_to_vector.glsl
|
||||
shaders/material/gpu_shader_material_axes_to_rotation.glsl
|
||||
shaders/material/gpu_shader_material_ambient_occlusion.glsl
|
||||
shaders/material/gpu_shader_material_attribute.glsl
|
||||
shaders/material/gpu_shader_material_axis_angle_to_rotation.glsl
|
||||
shaders/material/gpu_shader_material_background.glsl
|
||||
shaders/material/gpu_shader_material_bevel.glsl
|
||||
shaders/material/gpu_shader_material_wavelength.glsl
|
||||
shaders/material/gpu_shader_material_blackbody.glsl
|
||||
shaders/material/gpu_shader_material_bright_contrast.glsl
|
||||
shaders/material/gpu_shader_material_bump.glsl
|
||||
shaders/material/gpu_shader_material_camera.glsl
|
||||
shaders/material/gpu_shader_material_clamp.glsl
|
||||
shaders/material/gpu_shader_material_combine_color.glsl
|
||||
shaders/material/gpu_shader_material_combine_xyz.glsl
|
||||
shaders/material/gpu_shader_material_diffuse.glsl
|
||||
shaders/material/gpu_shader_material_displacement.glsl
|
||||
shaders/material/gpu_shader_material_eevee_specular.glsl
|
||||
shaders/material/gpu_shader_material_emission.glsl
|
||||
shaders/material/gpu_shader_material_euler_to_rotation.glsl
|
||||
shaders/material/gpu_shader_material_fractal_noise.glsl
|
||||
shaders/material/gpu_shader_material_fractal_voronoi.glsl
|
||||
shaders/material/gpu_shader_material_fresnel.glsl
|
||||
shaders/material/gpu_shader_material_gamma.glsl
|
||||
shaders/material/gpu_shader_material_geometry.glsl
|
||||
shaders/material/gpu_shader_material_glass.glsl
|
||||
shaders/material/gpu_shader_material_glossy.glsl
|
||||
shaders/material/gpu_shader_material_hair_info.glsl
|
||||
shaders/material/gpu_shader_material_hair.glsl
|
||||
shaders/material/gpu_shader_material_holdout.glsl
|
||||
shaders/material/gpu_shader_material_hue_sat_val.glsl
|
||||
shaders/material/gpu_shader_material_implicit_defaults.glsl
|
||||
shaders/material/gpu_shader_material_invert.glsl
|
||||
shaders/material/gpu_shader_material_invert_rotation.glsl
|
||||
shaders/material/gpu_shader_material_layer_weight.glsl
|
||||
shaders/material/gpu_shader_material_light_falloff.glsl
|
||||
shaders/material/gpu_shader_material_light_path.glsl
|
||||
shaders/material/gpu_shader_material_mapping.glsl
|
||||
shaders/material/gpu_shader_material_map_range.glsl
|
||||
shaders/material/gpu_shader_material_metallic.glsl
|
||||
shaders/material/gpu_shader_material_mix_color.glsl
|
||||
shaders/material/gpu_shader_material_mix_shader.glsl
|
||||
shaders/material/gpu_shader_material_noise.glsl
|
||||
shaders/material/gpu_shader_material_normal.glsl
|
||||
shaders/material/gpu_shader_material_normal_map.glsl
|
||||
shaders/material/gpu_shader_material_object_info.glsl
|
||||
shaders/material/gpu_shader_material_output_aov.glsl
|
||||
shaders/material/gpu_shader_material_output_material.glsl
|
||||
shaders/material/gpu_shader_material_output_world.glsl
|
||||
shaders/material/gpu_shader_material_particle_info.glsl
|
||||
shaders/material/gpu_shader_material_point_info.glsl
|
||||
shaders/material/gpu_shader_material_principled.glsl
|
||||
shaders/material/gpu_shader_material_quaternion_to_rotation.glsl
|
||||
shaders/material/gpu_shader_material_raycast.glsl
|
||||
shaders/material/gpu_shader_material_ray_portal.glsl
|
||||
shaders/material/gpu_shader_material_refraction.glsl
|
||||
shaders/material/gpu_shader_material_repeat_zone.glsl
|
||||
shaders/material/gpu_shader_material_rgb_to_bw.glsl
|
||||
shaders/material/gpu_shader_material_radial_tiling.glsl
|
||||
shaders/material/gpu_shader_material_radial_tiling_shared.glsl
|
||||
shaders/material/gpu_shader_material_rotate_rotation.glsl
|
||||
shaders/material/gpu_shader_material_rotate_vector.glsl
|
||||
shaders/material/gpu_shader_material_rotation_to_axis_angle.glsl
|
||||
shaders/material/gpu_shader_material_rotation_to_euler.glsl
|
||||
shaders/material/gpu_shader_material_rotation_to_quaternion.glsl
|
||||
shaders/material/gpu_shader_material_scene_time.glsl
|
||||
shaders/material/gpu_shader_material_separate_color.glsl
|
||||
shaders/material/gpu_shader_material_separate_xyz.glsl
|
||||
shaders/material/gpu_shader_material_set.glsl
|
||||
shaders/material/gpu_shader_material_shader_to_rgba.glsl
|
||||
shaders/material/gpu_shader_material_sheen.glsl
|
||||
shaders/material/gpu_shader_material_squeeze.glsl
|
||||
shaders/material/gpu_shader_material_subsurface_scattering.glsl
|
||||
shaders/material/gpu_shader_material_tangent.glsl
|
||||
shaders/material/gpu_shader_material_tex_brick.glsl
|
||||
shaders/material/gpu_shader_material_tex_checker.glsl
|
||||
shaders/material/gpu_shader_material_tex_environment.glsl
|
||||
shaders/material/gpu_shader_material_tex_gabor.glsl
|
||||
shaders/material/gpu_shader_material_tex_gradient.glsl
|
||||
shaders/material/gpu_shader_material_tex_image.glsl
|
||||
shaders/material/gpu_shader_material_tex_magic.glsl
|
||||
shaders/material/gpu_shader_material_tex_noise.glsl
|
||||
shaders/material/gpu_shader_material_tex_sky.glsl
|
||||
shaders/material/gpu_shader_material_texture_coordinates.glsl
|
||||
shaders/material/gpu_shader_material_tex_voronoi.glsl
|
||||
shaders/material/gpu_shader_material_tex_wave.glsl
|
||||
shaders/material/gpu_shader_material_tex_white_noise.glsl
|
||||
shaders/material/gpu_shader_material_toon.glsl
|
||||
shaders/material/gpu_shader_material_transform_utils.glsl
|
||||
shaders/material/gpu_shader_material_translucent.glsl
|
||||
shaders/material/gpu_shader_material_transparent.glsl
|
||||
shaders/material/gpu_shader_material_uv_map.glsl
|
||||
shaders/material/gpu_shader_material_vector_displacement.glsl
|
||||
shaders/material/gpu_shader_material_vector_math.glsl
|
||||
shaders/material/gpu_shader_material_vector_rotate.glsl
|
||||
shaders/material/gpu_shader_material_vertex_color.glsl
|
||||
shaders/material/gpu_shader_material_volume_absorption.glsl
|
||||
shaders/material/gpu_shader_material_volume_principled.glsl
|
||||
shaders/material/gpu_shader_material_volume_scatter.glsl
|
||||
shaders/material/gpu_shader_material_volume_coefficients.glsl
|
||||
shaders/material/gpu_shader_material_voronoi.glsl
|
||||
shaders/material/gpu_shader_material_wireframe.glsl
|
||||
|
||||
shaders/gpu_shader_gpencil_stroke_vert.glsl
|
||||
shaders/gpu_shader_gpencil_stroke_frag.glsl
|
||||
|
||||
shaders/gpu_shader_display_fallback_vert.glsl
|
||||
shaders/gpu_shader_display_fallback_frag.glsl
|
||||
|
||||
shaders/gpu_shader_cfg_world_clip_lib.glsl
|
||||
shaders/gpu_shader_colorspace_lib.glsl
|
||||
|
||||
shaders/gpu_shader_index_2d_array_points.glsl
|
||||
shaders/gpu_shader_index_2d_array_lines.glsl
|
||||
shaders/gpu_shader_index_2d_array_tris.glsl
|
||||
|
||||
shaders/gpu_shader_compat_glsl.glsl
|
||||
|
||||
shaders/gpu_shader_glsl_extension.glsl
|
||||
|
||||
GPU_shader_shared_utils.hh
|
||||
)
|
||||
|
||||
set(GLSL_TEST_SRC
|
||||
shaders/infos/gpu_shader_test_infos.hh
|
||||
|
||||
tests/shaders/gpu_math_test.bsl.hh
|
||||
tests/shaders/gpu_buffer_texture_test.glsl
|
||||
tests/shaders/gpu_compute_1d_test.glsl
|
||||
tests/shaders/gpu_compute_2d_test.glsl
|
||||
tests/shaders/gpu_compute_ibo_test.glsl
|
||||
tests/shaders/gpu_compute_ssbo_test.glsl
|
||||
tests/shaders/gpu_compute_vbo_test.glsl
|
||||
tests/shaders/gpu_compute_dummy_test.glsl
|
||||
tests/shaders/gpu_specialization_test.glsl
|
||||
tests/shaders/gpu_framebuffer_layer_viewport_test.glsl
|
||||
tests/shaders/gpu_framebuffer_subpass_input_test.glsl
|
||||
tests/shaders/gpu_push_constants_test.glsl
|
||||
tests/shaders/gpu_texture_test.glsl
|
||||
tests/shaders/gpu_texture_atomic_test.glsl
|
||||
)
|
||||
|
||||
set(MTL_BACKEND_GLSL_SRC
|
||||
metal/kernels/depth_2d_update_infos.hh
|
||||
metal/kernels/gpu_shader_fullscreen_blit_infos.hh
|
||||
|
||||
metal/kernels/depth_2d_update_float_frag.glsl
|
||||
metal/kernels/depth_2d_update_int24_frag.glsl
|
||||
metal/kernels/depth_2d_update_int32_frag.glsl
|
||||
metal/kernels/depth_2d_update_vert.glsl
|
||||
metal/kernels/gpu_shader_fullscreen_blit_vert.glsl
|
||||
metal/kernels/gpu_shader_fullscreen_blit_frag.glsl
|
||||
|
||||
shaders/gpu_shader_msl_atomic.msl
|
||||
shaders/gpu_shader_msl_attribute.msl
|
||||
shaders/gpu_shader_msl_builtin.msl
|
||||
shaders/gpu_shader_msl_image.msl
|
||||
shaders/gpu_shader_msl_matrix_legacy.msl
|
||||
shaders/gpu_shader_msl_matrix.msl
|
||||
shaders/gpu_shader_msl_sampler.msl
|
||||
shaders/gpu_shader_msl_string.msl
|
||||
shaders/gpu_shader_msl_types.msl
|
||||
shaders/gpu_shader_msl_types_legacy.msl
|
||||
shaders/gpu_shader_msl_defines.msl
|
||||
shaders/gpu_shader_msl_wrapper.msl
|
||||
shaders/gpu_shader_compat_msl.msl
|
||||
)
|
||||
|
||||
set(MSL_SRC
|
||||
metal/mtl_shader_shared.hh
|
||||
|
||||
metal/kernels/compute_texture_update.msl
|
||||
metal/kernels/compute_texture_read.msl
|
||||
)
|
||||
|
||||
set(VULKAN_BACKEND_GLSL_SRC
|
||||
vulkan/shaders/vk_backbuffer_blit_infos.hh
|
||||
|
||||
vulkan/shaders/vk_backbuffer_blit_comp.glsl
|
||||
)
|
||||
|
||||
if(WITH_GTESTS)
|
||||
if(WITH_GPU_BACKEND_TESTS)
|
||||
list(APPEND GLSL_SRC ${GLSL_TEST_SRC})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_METAL_BACKEND)
|
||||
list(APPEND GLSL_SRC ${MTL_BACKEND_GLSL_SRC})
|
||||
|
||||
set(MSL_C "")
|
||||
foreach(MSL_FILE ${MSL_SRC})
|
||||
data_to_c_simple(${MSL_FILE} MSL_C)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
list(APPEND GLSL_SRC ${VULKAN_BACKEND_GLSL_SRC})
|
||||
endif()
|
||||
|
||||
set(GLSL_INC
|
||||
.
|
||||
|
||||
intern
|
||||
shaders
|
||||
shaders/common
|
||||
shaders/infos
|
||||
shaders/material
|
||||
|
||||
vulkan/shaders
|
||||
)
|
||||
|
||||
set(GLSL_C "")
|
||||
foreach(GLSL_FILE ${GLSL_SRC})
|
||||
glsl_to_c(${GLSL_FILE} GLSL_C GLSL_INC)
|
||||
endforeach()
|
||||
|
||||
set(SHADER_C "")
|
||||
list(APPEND SHADER_C ${GLSL_C})
|
||||
if(WITH_METAL_BACKEND)
|
||||
list(APPEND SHADER_C ${MSL_C})
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_gpu_shaders "${SHADER_C}" "" "" "")
|
||||
blender_set_target_unity_build(bf_gpu_shaders 10)
|
||||
|
||||
list(APPEND LIB
|
||||
bf_gpu_shaders
|
||||
)
|
||||
|
||||
set(GLSL_SOURCE_CONTENT "")
|
||||
set(GLSL_METADATA_CONTENT "")
|
||||
set(GLSL_INFOS_CONTENT "")
|
||||
foreach(GLSL_FILE ${GLSL_SRC})
|
||||
get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME)
|
||||
string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME})
|
||||
string(APPEND GLSL_FILE_NAME_UNDERSCORES "_tmp")
|
||||
string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n")
|
||||
string(APPEND GLSL_METADATA_CONTENT "#include \"${GLSL_FILE}.hh\"\n")
|
||||
string(APPEND GLSL_INFOS_CONTENT "#include \"${GLSL_FILE}.info\"\n")
|
||||
endforeach()
|
||||
|
||||
set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_gpu_source_list.h")
|
||||
file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}")
|
||||
list(APPEND SRC ${glsl_source_list_file})
|
||||
set(glsl_metadata_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_gpu_metadata_list.hh")
|
||||
file(GENERATE OUTPUT ${glsl_metadata_list_file} CONTENT "${GLSL_METADATA_CONTENT}")
|
||||
list(APPEND SRC ${glsl_metadata_list_file})
|
||||
set(glsl_infos_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_gpu_infos_list.hh")
|
||||
file(GENERATE OUTPUT ${glsl_infos_list_file} CONTENT "${GLSL_INFOS_CONTENT}")
|
||||
list(APPEND SRC ${glsl_infos_list_file})
|
||||
list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if(WITH_MOD_FLUID)
|
||||
add_definitions(-DWITH_FLUID)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENSUBDIV)
|
||||
add_definitions(-DWITH_OPENSUBDIV)
|
||||
endif()
|
||||
|
||||
if(WITH_GPU_BACKEND_TESTS)
|
||||
add_definitions(-DWITH_GPU_BACKEND_TESTS)
|
||||
endif()
|
||||
|
||||
if(WITH_GTESTS)
|
||||
add_definitions(-DWITH_GTESTS)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::gpu ALIAS bf_gpu)
|
||||
target_link_libraries(bf_gpu PUBLIC
|
||||
bf_compositor_shaders
|
||||
bf_draw_shaders
|
||||
bf_gpu_shaders
|
||||
bf_imbuf_opencolorio_shaders
|
||||
)
|
||||
|
||||
# If `execinfo.h` exists on a *BSD system then also link in `libexecinfo`.
|
||||
# Needed for `backtrace` / `backtrace_symbols` (GNU extensions)
|
||||
# brought in by `blenlib/intern/system.cc`.
|
||||
if(HAVE_EXECINFO_H AND CMAKE_SYSTEM_NAME MATCHES "FreeBSD|NetBSD|OpenBSD|DragonFly")
|
||||
target_link_libraries(bf_gpu PUBLIC execinfo)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENSUBDIV)
|
||||
target_link_libraries(bf_gpu PUBLIC bf_osd_shaders)
|
||||
endif()
|
||||
|
||||
if(WITH_RENDERDOC)
|
||||
target_link_libraries(bf_gpu PUBLIC bf_intern_renderdoc_dynload)
|
||||
endif()
|
||||
|
||||
|
||||
if(CXX_WARN_NO_SUGGEST_OVERRIDE)
|
||||
target_compile_options(bf_gpu PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>)
|
||||
endif()
|
||||
|
||||
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_SRC "")
|
||||
set(TEST_INC "")
|
||||
set(TEST_LIB
|
||||
bf_intern_ghost
|
||||
bf_imbuf
|
||||
bf_windowmanager
|
||||
)
|
||||
|
||||
if(WITH_GPU_BACKEND_TESTS)
|
||||
list(APPEND TEST_SRC
|
||||
tests/buffer_texture_test.cc
|
||||
tests/compute_test.cc
|
||||
tests/framebuffer_test.cc
|
||||
tests/immediate_test.cc
|
||||
tests/index_buffer_test.cc
|
||||
tests/push_constants_test.cc
|
||||
tests/shader_create_info_test.cc
|
||||
tests/shader_preprocess_test.cc
|
||||
tests/shader_test.cc
|
||||
tests/specialization_constants_test.cc
|
||||
tests/state_blend_test.cc
|
||||
tests/storage_buffer_test.cc
|
||||
tests/texture_test.cc
|
||||
tests/texture_view_test.cc
|
||||
tests/vertex_buffer_test.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
list(APPEND TEST_SRC
|
||||
vulkan/tests/vk_data_conversion_test.cc
|
||||
vulkan/tests/vk_memory_layout_test.cc
|
||||
vulkan/render_graph/tests/vk_render_graph_test_compute.cc
|
||||
vulkan/render_graph/tests/vk_render_graph_test_present.cc
|
||||
vulkan/render_graph/tests/vk_render_graph_test_render.cc
|
||||
vulkan/render_graph/tests/vk_render_graph_test_scheduler.cc
|
||||
vulkan/render_graph/tests/vk_render_graph_test_transfer.cc
|
||||
|
||||
vulkan/render_graph/tests/vk_render_graph_test_types.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
set(TEST_COMMON_SRC
|
||||
tests/gpu_testing.cc
|
||||
tests/gpu_testing.hh
|
||||
)
|
||||
|
||||
blender_add_test_suite_lib(gpu
|
||||
"${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}" "${TEST_COMMON_SRC}"
|
||||
)
|
||||
endif()
|
||||
61
blender-5.2.0/source/blender/gpu/GPU_attribute_convert.hh
Normal file
61
blender-5.2.0/source/blender/gpu/GPU_attribute_convert.hh
Normal file
@@ -0,0 +1,61 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
struct PackedNormal {
|
||||
int x : 10;
|
||||
int y : 10;
|
||||
int z : 10;
|
||||
int w : 2; /* 0 by default, can manually set to { -2, -1, 0, 1 } */
|
||||
|
||||
PackedNormal() = default;
|
||||
PackedNormal(int _x, int _y, int _z, int _w = 0) : x(_x), y(_y), z(_z), w(_w) {}
|
||||
|
||||
/* Cast from int to float. */
|
||||
operator float4()
|
||||
{
|
||||
return float4(x, y, z, w);
|
||||
}
|
||||
};
|
||||
|
||||
inline int convert_normalized_f32_to_i10(float x)
|
||||
{
|
||||
/* OpenGL ES packs in a different order as desktop GL but component conversion is the same.
|
||||
* Of the code here, only PackedNormal needs to change. */
|
||||
constexpr int signed_int_10_max = 511;
|
||||
constexpr int signed_int_10_min = -512;
|
||||
|
||||
int qx = x * signed_int_10_max;
|
||||
return std::clamp(qx, signed_int_10_min, signed_int_10_max);
|
||||
}
|
||||
|
||||
template<typename GPUType> inline GPUType convert_normal(const float3 &src);
|
||||
|
||||
template<> inline PackedNormal convert_normal(const float3 &src)
|
||||
{
|
||||
return {
|
||||
convert_normalized_f32_to_i10(src[0]),
|
||||
convert_normalized_f32_to_i10(src[1]),
|
||||
convert_normalized_f32_to_i10(src[2]),
|
||||
0,
|
||||
};
|
||||
}
|
||||
|
||||
template<> inline short4 convert_normal(const float3 &src)
|
||||
{
|
||||
return short4(src * float(std::numeric_limits<short>::max()), 0);
|
||||
}
|
||||
|
||||
template<typename GPUType> void convert_normals(Span<float3> src, MutableSpan<GPUType> dst);
|
||||
|
||||
} // namespace blender::gpu
|
||||
439
blender-5.2.0/source/blender/gpu/GPU_batch.hh
Normal file
439
blender-5.2.0/source/blender/gpu/GPU_batch.hh
Normal file
@@ -0,0 +1,439 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU geometry batch.
|
||||
*
|
||||
* Contains Vertex Buffers, Index Buffers, and Shader reference, altogether representing a drawable
|
||||
* entity. It is meant to be used for drawing large (> 1000 vertices) reusable (drawn multiple
|
||||
* times) model with complex data layout. In other words, it is meant for all cases where the
|
||||
* immediate drawing module (imm) is inadequate.
|
||||
*
|
||||
* Vertex & Index Buffers can be owned by a batch. In such case they will be freed when the batch
|
||||
* gets cleared or discarded.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_storage_buffer.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Shader;
|
||||
} // namespace gpu
|
||||
|
||||
constexpr static int GPU_BATCH_VBO_MAX_LEN = 16;
|
||||
constexpr static int GPU_BATCH_VAO_STATIC_LEN = 3;
|
||||
constexpr static int GPU_BATCH_VAO_DYN_ALLOC_COUNT = 16;
|
||||
|
||||
enum GPUBatchFlag {
|
||||
/** Invalid default state. */
|
||||
GPU_BATCH_INVALID = 0,
|
||||
|
||||
/** gpu::VertBuf ownership. (One bit per vbo) */
|
||||
GPU_BATCH_OWNS_VBO = (1 << 0),
|
||||
GPU_BATCH_OWNS_VBO_MAX = (GPU_BATCH_OWNS_VBO << (GPU_BATCH_VBO_MAX_LEN - 1)),
|
||||
GPU_BATCH_OWNS_VBO_ANY = ((GPU_BATCH_OWNS_VBO << GPU_BATCH_VBO_MAX_LEN) - 1),
|
||||
/** gpu::IndexBuf ownership. */
|
||||
GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_VBO_MAX << 1),
|
||||
|
||||
/** Has been initialized. At least one VBO is set. */
|
||||
GPU_BATCH_INIT = (1 << 26),
|
||||
/** Batch is initialized but its VBOs are still being populated. (optional) */
|
||||
GPU_BATCH_BUILDING = (1 << 26),
|
||||
/** Cached data need to be rebuild. (VAO, PSO, ...) */
|
||||
GPU_BATCH_DIRTY = (1 << 27),
|
||||
};
|
||||
|
||||
#define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID
|
||||
|
||||
BLI_STATIC_ASSERT(GPU_BATCH_OWNS_INDEX < GPU_BATCH_INIT,
|
||||
"GPUBatchFlag: Error: status flags are shadowed by the ownership bits!")
|
||||
|
||||
ENUM_OPERATORS(GPUBatchFlag)
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/**
|
||||
* Base class which is then specialized for each implementation (GL, VK, ...).
|
||||
* TODO(fclem): Make the content of this struct hidden and expose getters/setters.
|
||||
*
|
||||
* Do not allocate manually as the real struct is bigger (i.e: GLBatch). This is only
|
||||
* the common and "public" part of the struct. Use `GPU_batch_calloc()` and `GPU_batch_create()`
|
||||
* instead.
|
||||
*/
|
||||
class Batch {
|
||||
public:
|
||||
/** verts[0] is required, others can be nullptr */
|
||||
gpu::VertBuf *verts[GPU_BATCH_VBO_MAX_LEN];
|
||||
/** nullptr if element list not needed */
|
||||
gpu::IndexBuf *elem;
|
||||
/** Number of vertices to draw for procedural drawcalls. -1 otherwise. */
|
||||
int32_t procedural_vertices;
|
||||
/** Bookkeeping. */
|
||||
GPUBatchFlag flag;
|
||||
/** Type of geometry to draw. */
|
||||
GPUPrimType prim_type;
|
||||
/** Current assigned shader. DEPRECATED. Here only for uniform binding. */
|
||||
gpu::Shader *shader;
|
||||
|
||||
virtual ~Batch() = default;
|
||||
|
||||
virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0;
|
||||
virtual void draw_indirect(gpu::StorageBuf *indirect_buf, intptr_t offset) = 0;
|
||||
virtual void multi_draw_indirect(gpu::StorageBuf *indirect_buf,
|
||||
int count,
|
||||
intptr_t offset,
|
||||
intptr_t stride) = 0;
|
||||
|
||||
uint32_t vertex_count_get() const
|
||||
{
|
||||
if (elem) {
|
||||
return elem_()->index_len_get();
|
||||
}
|
||||
return verts_(0)->vertex_len;
|
||||
}
|
||||
|
||||
/* Convenience casts. */
|
||||
IndexBuf *elem_() const
|
||||
{
|
||||
return elem;
|
||||
}
|
||||
VertBuf *verts_(const int index) const
|
||||
{
|
||||
return verts[index];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Allocate a #gpu::Batch with a cleared state.
|
||||
* The returned #gpu::Batch needs to be passed to `GPU_batch_init` before being usable.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_calloc();
|
||||
|
||||
/**
|
||||
* Creates a #gpu::Batch with explicit buffer ownership.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_create_ex(GPUPrimType primitive_type,
|
||||
gpu::VertBuf *vertex_buf,
|
||||
gpu::IndexBuf *index_buf,
|
||||
GPUBatchFlag owns_flag);
|
||||
|
||||
gpu::Batch *GPU_batch_create_procedural(GPUPrimType primitive_type, int32_t vertex_count);
|
||||
|
||||
/**
|
||||
* Creates a #gpu::Batch without buffer ownership.
|
||||
*/
|
||||
#define GPU_batch_create(primitive_type, vertex_buf, index_buf) \
|
||||
GPU_batch_create_ex(primitive_type, vertex_buf, index_buf, (GPUBatchFlag)0)
|
||||
|
||||
/**
|
||||
* Initialize a cleared #gpu::Batch with explicit buffer ownership.
|
||||
* A #gpu::Batch is in cleared state if it was just allocated using `GPU_batch_calloc()`
|
||||
* or cleared using `GPU_batch_clear()`.
|
||||
*/
|
||||
void GPU_batch_init_ex(gpu::Batch *batch,
|
||||
GPUPrimType primitive_type,
|
||||
gpu::VertBuf *vertex_buf,
|
||||
gpu::IndexBuf *index_buf,
|
||||
GPUBatchFlag owns_flag);
|
||||
/**
|
||||
* Initialize a cleared #gpu::Batch without buffer ownership.
|
||||
* A #gpu::Batch is in cleared state if it was just allocated using `GPU_batch_calloc()`
|
||||
* or cleared using `GPU_batch_clear()`.
|
||||
*/
|
||||
#define GPU_batch_init(batch, primitive_type, vertex_buf, index_buf) \
|
||||
GPU_batch_init_ex(batch, primitive_type, vertex_buf, index_buf, (GPUBatchFlag)0)
|
||||
|
||||
/**
|
||||
* DEPRECATED: It is easy to loose ownership with this. To be removed.
|
||||
* This will share the VBOs with the new batch.
|
||||
*/
|
||||
void GPU_batch_copy(gpu::Batch *batch_dst, gpu::Batch *batch_src);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Deletion
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Clear a #gpu::Batch without freeing its own memory.
|
||||
* The #gpu::Batch can then be reused using `GPU_batch_init()`.
|
||||
* Discards all owned vertex and index buffers.
|
||||
*/
|
||||
void GPU_batch_clear(gpu::Batch *batch);
|
||||
|
||||
void GPU_batch_zero(gpu::Batch *batch);
|
||||
|
||||
#define GPU_BATCH_CLEAR_SAFE(batch) \
|
||||
do { \
|
||||
if (batch != nullptr) { \
|
||||
GPU_batch_clear(batch); \
|
||||
GPU_batch_zero(batch); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Free a #gpu::Batch object.
|
||||
* Discards all owned vertex and index buffers.
|
||||
*/
|
||||
void GPU_batch_discard(gpu::Batch *batch);
|
||||
|
||||
#define GPU_BATCH_DISCARD_SAFE(batch) \
|
||||
do { \
|
||||
if (batch != nullptr) { \
|
||||
GPU_batch_discard(batch); \
|
||||
batch = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Buffers Management
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Add the given \a vertex_buf as vertex buffer to a #gpu::Batch.
|
||||
* \return the index of verts in the batch.
|
||||
*/
|
||||
int GPU_batch_vertbuf_add(gpu::Batch *batch, gpu::VertBuf *vertex_buf, bool own_vbo);
|
||||
|
||||
/**
|
||||
* Set the index buffer of a #gpu::Batch.
|
||||
* \note Override any previously assigned index buffer (and free it if owned).
|
||||
*/
|
||||
void GPU_batch_elembuf_set(gpu::Batch *batch, gpu::IndexBuf *index_buf, bool own_ibo);
|
||||
|
||||
/**
|
||||
* Returns true if the #GPUbatch has \a vertex_buf in its vertex buffer list.
|
||||
* \note The search is only conducted on the non-instance rate vertex buffer list.
|
||||
*/
|
||||
bool GPU_batch_vertbuf_has(const gpu::Batch *batch, const gpu::VertBuf *vertex_buf);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shader Binding & Uniforms
|
||||
*
|
||||
* TODO(fclem): This whole section should be removed. See the other `TODO`s in this section.
|
||||
* This is because we want to remove #gpu::Batch.shader to avoid usage mistakes.
|
||||
* Interacting directly with the #gpu::Shader provide a clearer interface and less error-prone.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Sets the shader to be drawn with this #gpu::Batch.
|
||||
* \note This need to be called first for the `GPU_batch_uniform_*` functions to work.
|
||||
*/
|
||||
/* TODO(fclem): These should be removed and replaced by `GPU_shader_bind()`. */
|
||||
void GPU_batch_set_shader(gpu::Batch *batch,
|
||||
gpu::Shader *shader,
|
||||
const gpu::shader::SpecializationConstants *constants_state = nullptr);
|
||||
void GPU_batch_program_set_builtin(gpu::Batch *batch, GPUBuiltinShader shader_id);
|
||||
void GPU_batch_program_set_builtin_with_config(gpu::Batch *batch,
|
||||
GPUBuiltinShader shader_id,
|
||||
GPUShaderConfig sh_cfg);
|
||||
/**
|
||||
* Bind program bound to IMM (immediate mode) to the #gpu::Batch.
|
||||
*
|
||||
* XXX: Use this with much care. Drawing with the #gpu::Batch API is not compatible with
|
||||
* IMM. DO NOT DRAW WITH THE BATCH BEFORE CALLING #immUnbindProgram.
|
||||
*/
|
||||
void GPU_batch_program_set_imm_shader(gpu::Batch *batch);
|
||||
|
||||
/**
|
||||
* Set uniform variables for the shader currently bound to the #gpu::Batch.
|
||||
*/
|
||||
/* TODO(fclem): These need to be replaced by GPU_shader_uniform_* with explicit shader. */
|
||||
#define GPU_batch_uniform_1i(batch, name, x) GPU_shader_uniform_1i((batch)->shader, name, x);
|
||||
#define GPU_batch_uniform_1b(batch, name, x) GPU_shader_uniform_1b((batch)->shader, name, x);
|
||||
#define GPU_batch_uniform_1f(batch, name, x) GPU_shader_uniform_1f((batch)->shader, name, x);
|
||||
#define GPU_batch_uniform_2f(batch, name, x, y) GPU_shader_uniform_2f((batch)->shader, name, x, y);
|
||||
#define GPU_batch_uniform_3f(batch, name, x, y, z) \
|
||||
GPU_shader_uniform_3f((batch)->shader, name, x, y, z);
|
||||
#define GPU_batch_uniform_4f(batch, name, x, y, z, w) \
|
||||
GPU_shader_uniform_4f((batch)->shader, name, x, y, z, w);
|
||||
#define GPU_batch_uniform_2fv(batch, name, val) GPU_shader_uniform_2fv((batch)->shader, name, val);
|
||||
#define GPU_batch_uniform_3fv(batch, name, val) GPU_shader_uniform_3fv((batch)->shader, name, val);
|
||||
#define GPU_batch_uniform_4fv(batch, name, val) GPU_shader_uniform_4fv((batch)->shader, name, val);
|
||||
#define GPU_batch_uniform_2fv_array(batch, name, len, val) \
|
||||
GPU_shader_uniform_2fv_array((batch)->shader, name, len, val);
|
||||
#define GPU_batch_uniform_4fv_array(batch, name, len, val) \
|
||||
GPU_shader_uniform_4fv_array((batch)->shader, name, len, val);
|
||||
#define GPU_batch_uniform_mat4(batch, name, val) \
|
||||
GPU_shader_uniform_mat4((batch)->shader, name, val);
|
||||
#define GPU_batch_uniformbuf_bind(batch, name, ubo) \
|
||||
GPU_uniformbuf_bind(ubo, GPU_shader_get_ubo_binding((batch)->shader, name));
|
||||
#define GPU_batch_texture_bind(batch, name, tex) \
|
||||
GPU_texture_bind(tex, GPU_shader_get_sampler_binding((batch)->shader, name));
|
||||
|
||||
/**
|
||||
* Bind vertex and index buffers to SSBOs using `Frequency::GEOMETRY`.
|
||||
*/
|
||||
void GPU_batch_bind_as_resources(gpu::Batch *batch,
|
||||
gpu::Shader *shader,
|
||||
const gpu::shader::SpecializationConstants *constants = nullptr);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shader Binding & Uniforms
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Draw the #gpu::Batch with vertex count and instance count from its vertex buffers
|
||||
* lengths. Ensures the associated shader is bound. TODO(fclem) remove this behavior.
|
||||
*/
|
||||
void GPU_batch_draw(gpu::Batch *batch);
|
||||
|
||||
/**
|
||||
* Draw the #gpu::Batch with vertex count and instance count from its vertex buffers
|
||||
* lengths. Ensures the associated shader is bound. TODO(fclem) remove this behavior.
|
||||
*
|
||||
* A \a vertex_count of 0 will use the default number of vertex.
|
||||
* The \a vertex_first sets the start of the instance-rate attributes.
|
||||
*
|
||||
* \note No out-of-bound access check is made on the vertex buffers since they are tricky to
|
||||
* detect. Double check that the range of vertex has data or that the data isn't read by the
|
||||
* shader.
|
||||
*/
|
||||
void GPU_batch_draw_range(gpu::Batch *batch, int vertex_first, int vertex_count);
|
||||
|
||||
/**
|
||||
* Draw multiple instances of the #gpu::Batch with custom instance range.
|
||||
* Ensures the associated shader is bound. TODO(fclem) remove this behavior.
|
||||
*
|
||||
* An \a instance_count of 0 will use the default number of instances.
|
||||
* The \a instance_first sets the start of the instance-rate attributes.
|
||||
*
|
||||
* \note this can be used even if the #gpu::Batch contains no instance-rate attributes.
|
||||
* \note No out-of-bound access check is made on the vertex buffers since they are tricky to
|
||||
* detect. Double check that the range of vertex has data or that the data isn't read by the
|
||||
* shader.
|
||||
*/
|
||||
void GPU_batch_draw_instance_range(gpu::Batch *batch, int instance_first, int instance_count);
|
||||
|
||||
/**
|
||||
* Draw the #gpu::Batch custom parameters.
|
||||
* IMPORTANT: This does not bind/unbind shader and does not call GPU_matrix_bind().
|
||||
*
|
||||
* A \a vertex_count of 0 will use the default number of vertex.
|
||||
* An \a instance_count of 0 will use the default number of instances.
|
||||
*
|
||||
* \note No out-of-bound access check is made on the vertex buffers since they are tricky to
|
||||
* detect. Double check that the range of vertex has data or that the data isn't read by the
|
||||
* shader.
|
||||
*/
|
||||
void GPU_batch_draw_advanced(
|
||||
gpu::Batch *batch, int vertex_first, int vertex_count, int instance_first, int instance_count);
|
||||
|
||||
/**
|
||||
* Issue a single draw call using arguments sourced from a #gpu::StorageBuf.
|
||||
* The argument are expected to be valid for the type of geometry contained by this
|
||||
* #gpu::Batch (index or non-indexed).
|
||||
*
|
||||
* The indirect buffer needs to be synced after filling its contents and before calling this
|
||||
* function using `GPU_storagebuf_sync_as_indirect_buffer`.
|
||||
*
|
||||
* For more info see the GL documentation:
|
||||
* https://registry.khronos.org/OpenGL-Refpages/gl4/html/glDrawArraysIndirect.xhtml
|
||||
*/
|
||||
void GPU_batch_draw_indirect(gpu::Batch *batch, gpu::StorageBuf *indirect_buf, intptr_t offset);
|
||||
|
||||
/**
|
||||
* Issue \a count draw calls using arguments sourced from a #gpu::StorageBuf.
|
||||
* The \a stride (in bytes) control the spacing between each command description.
|
||||
* The argument are expected to be valid for the type of geometry contained by this
|
||||
* #gpu::Batch (index or non-indexed).
|
||||
*
|
||||
* The indirect buffer needs to be synced after filling its contents and before calling this
|
||||
* function using `GPU_storagebuf_sync_as_indirect_buffer`.
|
||||
*
|
||||
* For more info see the GL documentation:
|
||||
* https://registry.khronos.org/OpenGL-Refpages/gl4/html/glMultiDrawArraysIndirect.xhtml
|
||||
*/
|
||||
void GPU_batch_multi_draw_indirect(
|
||||
gpu::Batch *batch, gpu::StorageBuf *indirect_buf, int count, intptr_t offset, intptr_t stride);
|
||||
|
||||
/**
|
||||
* Return indirect draw call parameters for this #gpu::Batch.
|
||||
* NOTE: \a r_base_index is set to -1 if not using an index buffer.
|
||||
*/
|
||||
void GPU_batch_draw_parameter_get(gpu::Batch *batch,
|
||||
int *r_vertex_count,
|
||||
int *r_vertex_first,
|
||||
int *r_base_index,
|
||||
int *r_instance_count);
|
||||
|
||||
/**
|
||||
* Return vertex range for this #gpu::Batch when using primitive expansions.
|
||||
*/
|
||||
IndexRange GPU_batch_draw_expanded_parameter_get(GPUPrimType input_prim_type,
|
||||
GPUPrimType output_prim_type,
|
||||
int vertex_count,
|
||||
int vertex_first,
|
||||
int output_primitive_cout);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Procedural drawing
|
||||
*
|
||||
* A draw-call always need a batch to be issued.
|
||||
* These are dummy batches that contains no vertex data and can be used to render geometry
|
||||
* without per vertex inputs.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Batch with no attributes, suited for rendering procedural geometry.
|
||||
* IMPORTANT: The returned batch is only valid for the current context.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_procedural_points_get();
|
||||
|
||||
/**
|
||||
* Batch with no attributes, suited for rendering procedural geometry.
|
||||
* IMPORTANT: The returned batch is only valid for the current context.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_procedural_lines_get();
|
||||
|
||||
/**
|
||||
* Batch with no attributes, suited for rendering procedural geometry.
|
||||
* IMPORTANT: The returned batch is only valid for the current context.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_procedural_triangles_get();
|
||||
|
||||
/**
|
||||
* Batch with no attributes, suited for rendering procedural geometry.
|
||||
* IMPORTANT: The returned batch is only valid for the current context.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_procedural_triangle_strips_get();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module init/exit
|
||||
* \{ */
|
||||
|
||||
void gpu_batch_init();
|
||||
void gpu_batch_exit();
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
42
blender-5.2.0/source/blender/gpu/GPU_batch_presets.hh
Normal file
42
blender-5.2.0/source/blender/gpu/GPU_batch_presets.hh
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Batched geometry rendering is powered by the GPU library.
|
||||
* This file contains any additions or modifications specific to Blender.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
class StorageBuf;
|
||||
} // namespace gpu
|
||||
|
||||
/* `gpu_batch_presets.cc` */
|
||||
|
||||
/* Replacement for #gluSphere */
|
||||
|
||||
gpu::Batch *GPU_batch_preset_sphere(int lod) ATTR_WARN_UNUSED_RESULT;
|
||||
gpu::Batch *GPU_batch_preset_sphere_wire(int lod) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* To be used with procedural placement inside shader.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_preset_quad();
|
||||
|
||||
void gpu_batch_presets_init();
|
||||
/* Registers batch to be destroyed at exit time. */
|
||||
void gpu_batch_presets_register(gpu::Batch *preset_batch);
|
||||
/* Registers buffer to be destroyed at exit time. */
|
||||
void gpu_batch_storage_buffer_register(gpu::StorageBuf *preset_buffer);
|
||||
void gpu_batch_presets_exit();
|
||||
|
||||
} // namespace blender
|
||||
54
blender-5.2.0/source/blender/gpu/GPU_batch_utils.hh
Normal file
54
blender-5.2.0/source/blender/gpu/GPU_batch_utils.hh
Normal file
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct rctf;
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
}
|
||||
|
||||
/* `gpu_batch_utils.cc` */
|
||||
|
||||
/**
|
||||
* Creates triangles from a byte-array of polygons.
|
||||
*
|
||||
* See 'make_shape_2d_from_blend.py' utility to create data to pass to this function.
|
||||
*
|
||||
* \param polys_flat: Pairs of X, Y coordinates (repeating to signify closing the polygon).
|
||||
* \param polys_flat_len: Length of the array (must be an even number).
|
||||
* \param rect: Optional region to map the byte 0..255 coords to. When not set use -1..1.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_tris_from_poly_2d_encoded(const uchar *polys_flat,
|
||||
uint polys_flat_len,
|
||||
const rctf *rect) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1);
|
||||
gpu::Batch *GPU_batch_wire_from_poly_2d_encoded(const uchar *polys_flat,
|
||||
uint polys_flat_len,
|
||||
const rctf *rect) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Create a batch of a unit cube (-1..+1).
|
||||
* Only has position attribute.
|
||||
*/
|
||||
gpu::Batch *GPU_batch_unit_cube() ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Replacement for #gluSphere.
|
||||
*
|
||||
* \note Only use by draw manager. Use the presets function instead for interface.
|
||||
*/
|
||||
gpu::Batch *gpu_batch_sphere(int lat_res, int lon_res) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
} // namespace blender
|
||||
81
blender-5.2.0/source/blender/gpu/GPU_capabilities.hh
Normal file
81
blender-5.2.0/source/blender/gpu/GPU_capabilities.hh
Normal file
@@ -0,0 +1,81 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU Capabilities & workarounds
|
||||
* This module expose the reported implementation limits & enabled
|
||||
* workaround for drivers that needs specific code-paths.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
int GPU_max_texture_size();
|
||||
int GPU_max_texture_3d_size();
|
||||
uint32_t GPU_max_buffer_texture_size();
|
||||
int GPU_max_texture_layers();
|
||||
int GPU_max_textures();
|
||||
int GPU_max_images();
|
||||
int GPU_max_work_group_count(int index);
|
||||
int GPU_max_work_group_size(int index);
|
||||
int GPU_max_uniforms_vert();
|
||||
int GPU_max_uniforms_frag();
|
||||
int GPU_max_batch_indices();
|
||||
int GPU_max_batch_vertices();
|
||||
int GPU_max_vertex_attribs();
|
||||
int GPU_max_varying_floats();
|
||||
int GPU_max_shader_storage_buffer_bindings();
|
||||
int GPU_max_compute_shader_storage_blocks();
|
||||
size_t GPU_max_uniform_buffer_size();
|
||||
size_t GPU_max_storage_buffer_size();
|
||||
/* Used when binding subrange of SSBOs. In bytes.
|
||||
* The start of the range must be aligned with this value. */
|
||||
size_t GPU_storage_buffer_alignment();
|
||||
|
||||
int GPU_extensions_len();
|
||||
const char *GPU_extension_get(int i);
|
||||
|
||||
int GPU_texture_size_with_limit(int res);
|
||||
|
||||
/**
|
||||
* Returns whether it should be "safe" to use texture of a given size.
|
||||
*
|
||||
* The heuristic is that maybe allocating texture that is 25% of
|
||||
* #GPU_max_texture_size squared is fine. Note that the actual texture creation
|
||||
* can still fail even if deemed "safe" by this function, depending on current memory
|
||||
* usage, texture format, etc.
|
||||
*/
|
||||
bool GPU_is_safe_texture_size(int width, int height);
|
||||
|
||||
bool GPU_use_subprocess_compilation();
|
||||
int GPU_max_parallel_compilations();
|
||||
|
||||
bool GPU_stencil_clasify_buffer_workaround();
|
||||
bool GPU_depth_blitting_workaround();
|
||||
bool GPU_use_main_context_workaround();
|
||||
bool GPU_use_hq_normals_workaround();
|
||||
|
||||
bool GPU_geometry_shader_support();
|
||||
bool GPU_hdr_support();
|
||||
bool GPU_stencil_export_support();
|
||||
|
||||
bool GPU_mem_stats_supported();
|
||||
void GPU_mem_stats_get(int *r_totalmem, int *r_freemem);
|
||||
|
||||
/**
|
||||
* Return support for the active context + window.
|
||||
*/
|
||||
bool GPU_stereo_quadbuffer_support();
|
||||
|
||||
int GPU_minimum_per_vertex_stride();
|
||||
|
||||
/** WARNING: Should only be called at startup from creator_args. Never call it at runtime. */
|
||||
void GPU_compilation_subprocess_override_set(int count);
|
||||
|
||||
} // namespace blender
|
||||
19
blender-5.2.0/source/blender/gpu/GPU_common.hh
Normal file
19
blender-5.2.0/source/blender/gpu/GPU_common.hh
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define PROGRAM_NO_OPTI 0
|
||||
// #define GPU_NO_USE_PY_REFERENCES
|
||||
|
||||
/* GPU_INLINE */
|
||||
#if defined(_MSC_VER)
|
||||
# define GPU_INLINE static __forceinline
|
||||
#else
|
||||
# define GPU_INLINE static inline __attribute__((always_inline)) __attribute__((__unused__))
|
||||
#endif
|
||||
292
blender-5.2.0/source/blender/gpu/GPU_common_types.hh
Normal file
292
blender-5.2.0/source/blender/gpu/GPU_common_types.hh
Normal file
@@ -0,0 +1,292 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Describes the load operation of a frame-buffer attachment at the start of a render pass.
|
||||
*/
|
||||
enum GPULoadOp {
|
||||
/**
|
||||
* Clear the frame-buffer attachment using the clear value.
|
||||
*/
|
||||
GPU_LOADACTION_CLEAR = 0,
|
||||
/**
|
||||
* Load the value from the attached texture.
|
||||
* Cannot be used with memoryless attachments.
|
||||
* Slower than `GPU_LOADACTION_CLEAR` or `GPU_LOADACTION_DONT_CARE`.
|
||||
*/
|
||||
GPU_LOADACTION_LOAD,
|
||||
/**
|
||||
* Do not care about the content of the attachment when the render pass starts.
|
||||
* Useful if only the values being written are important.
|
||||
* Faster than `GPU_LOADACTION_CLEAR`.
|
||||
*/
|
||||
GPU_LOADACTION_DONT_CARE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the store operation of a frame-buffer attachment at the end of a render pass.
|
||||
*/
|
||||
enum GPUStoreOp {
|
||||
/**
|
||||
* Do not care about the content of the attachment when the render pass ends.
|
||||
* Useful if only the values being written are important.
|
||||
* Cannot be used with memoryless attachments.
|
||||
*/
|
||||
GPU_STOREACTION_STORE = 0,
|
||||
/**
|
||||
* The result of the rendering for this attachment will be discarded.
|
||||
* No writes to the texture memory will be done which makes it faster than
|
||||
* `GPU_STOREACTION_STORE`.
|
||||
* IMPORTANT: The actual values of the attachment is to be considered undefined.
|
||||
* Only to be used on transient attachment that are only used within the boundaries of
|
||||
* a render pass (ex.: Unneeded depth buffer result).
|
||||
*/
|
||||
GPU_STOREACTION_DONT_CARE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the state of a frame-buffer attachment during a sub-pass.
|
||||
*
|
||||
* NOTE: Until this is correctly implemented in all backend, reading and writing from the
|
||||
* same attachment will not work. Although there is no case where it would currently be useful.
|
||||
*/
|
||||
enum GPUAttachmentState {
|
||||
/** Attachment will not be written during rendering. */
|
||||
GPU_ATTACHMENT_IGNORE = 0,
|
||||
/** Attachment will be written during render sub-pass. This also works with blending. */
|
||||
GPU_ATTACHMENT_WRITE,
|
||||
/** Attachment is used as input in the fragment shader. Incompatible with depth on Metal. */
|
||||
GPU_ATTACHMENT_READ,
|
||||
};
|
||||
|
||||
enum GPUFrontFace {
|
||||
GPU_CLOCKWISE,
|
||||
GPU_COUNTERCLOCKWISE,
|
||||
};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
enum class ShaderStage : uint8_t {
|
||||
VERTEX = 1 << 0,
|
||||
FRAGMENT = 1 << 1,
|
||||
COMPUTE = 2 << 1,
|
||||
ANY = (ShaderStage::VERTEX | ShaderStage::FRAGMENT | ShaderStage::COMPUTE),
|
||||
};
|
||||
ENUM_OPERATORS(ShaderStage);
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
namespace gpu::shader {
|
||||
|
||||
enum class Type : int8_t {
|
||||
/* Types supported natively across all GPU back-ends. */
|
||||
float_t = 0,
|
||||
float2_t,
|
||||
float3_t,
|
||||
float4_t,
|
||||
float3x3_t,
|
||||
float4x4_t,
|
||||
uint_t,
|
||||
uint2_t,
|
||||
uint3_t,
|
||||
uint4_t,
|
||||
int_t,
|
||||
int2_t,
|
||||
int3_t,
|
||||
int4_t,
|
||||
bool_t,
|
||||
/* Additionally supported types to enable data optimization and native
|
||||
* support in some GPU back-ends.
|
||||
* NOTE: These types must be representable in all APIs. E.g. `VEC3_101010I2` is aliased as vec3
|
||||
* in the GL back-end, as implicit type conversions from packed normal attribute data to vec3 is
|
||||
* supported. UCHAR/CHAR types are natively supported in Metal and can be used to avoid
|
||||
* additional data conversions for `GPU_COMP_U8` vertex attributes. */
|
||||
float3_10_10_10_2_t,
|
||||
uchar_t,
|
||||
uchar2_t,
|
||||
uchar3_t,
|
||||
uchar4_t,
|
||||
char_t,
|
||||
char2_t,
|
||||
char3_t,
|
||||
char4_t,
|
||||
ushort_t,
|
||||
ushort2_t,
|
||||
ushort3_t,
|
||||
ushort4_t,
|
||||
short_t,
|
||||
short2_t,
|
||||
short3_t,
|
||||
short4_t
|
||||
};
|
||||
|
||||
BLI_INLINE int to_component_count(const Type &type)
|
||||
{
|
||||
switch (type) {
|
||||
case Type::float_t:
|
||||
case Type::uint_t:
|
||||
case Type::int_t:
|
||||
case Type::bool_t:
|
||||
return 1;
|
||||
case Type::float2_t:
|
||||
case Type::uint2_t:
|
||||
case Type::int2_t:
|
||||
return 2;
|
||||
case Type::float3_t:
|
||||
case Type::uint3_t:
|
||||
case Type::int3_t:
|
||||
return 3;
|
||||
case Type::float4_t:
|
||||
case Type::uint4_t:
|
||||
case Type::int4_t:
|
||||
return 4;
|
||||
case Type::float3x3_t:
|
||||
return 9;
|
||||
case Type::float4x4_t:
|
||||
return 16;
|
||||
/* Alias special types. */
|
||||
case Type::uchar_t:
|
||||
case Type::ushort_t:
|
||||
case Type::char_t:
|
||||
case Type::short_t:
|
||||
return 1;
|
||||
case Type::uchar2_t:
|
||||
case Type::ushort2_t:
|
||||
case Type::char2_t:
|
||||
case Type::short2_t:
|
||||
return 2;
|
||||
case Type::uchar3_t:
|
||||
case Type::ushort3_t:
|
||||
case Type::char3_t:
|
||||
case Type::short3_t:
|
||||
return 3;
|
||||
case Type::uchar4_t:
|
||||
case Type::ushort4_t:
|
||||
case Type::char4_t:
|
||||
case Type::short4_t:
|
||||
return 4;
|
||||
case Type::float3_10_10_10_2_t:
|
||||
return 3;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct SpecializationConstant {
|
||||
struct Value {
|
||||
union {
|
||||
uint32_t u;
|
||||
int32_t i;
|
||||
float f;
|
||||
};
|
||||
|
||||
bool operator==(const Value &other) const
|
||||
{
|
||||
return u == other.u;
|
||||
}
|
||||
|
||||
uint64_t hash() const
|
||||
{
|
||||
return uint64_t(u);
|
||||
}
|
||||
};
|
||||
|
||||
Type type;
|
||||
StringRefNull name;
|
||||
Value value;
|
||||
|
||||
SpecializationConstant() = default;
|
||||
|
||||
SpecializationConstant(const char *name, uint32_t value) : type(Type::uint_t), name(name)
|
||||
{
|
||||
this->value.u = value;
|
||||
}
|
||||
|
||||
SpecializationConstant(const char *name, int value) : type(Type::int_t), name(name)
|
||||
{
|
||||
this->value.i = value;
|
||||
}
|
||||
|
||||
SpecializationConstant(const char *name, float value) : type(Type::float_t), name(name)
|
||||
{
|
||||
this->value.f = value;
|
||||
}
|
||||
|
||||
SpecializationConstant(const char *name, bool value) : type(Type::bool_t), name(name)
|
||||
{
|
||||
this->value.u = value ? 1 : 0;
|
||||
}
|
||||
|
||||
bool operator==(const SpecializationConstant &b) const
|
||||
{
|
||||
return this->type == b.type && this->name == b.name && this->value == b.value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Specialization constants as a Struct-of-Arrays. Allow simpler comparison and reset.
|
||||
* The backend is free to implement their support as they see fit.
|
||||
*/
|
||||
struct SpecializationConstants {
|
||||
Vector<gpu::shader::Type, 8> types;
|
||||
/* Current values set by `GPU_shader_constant_*()` call. The backend can choose to interpret
|
||||
* that however it wants (i.e: bind another shader instead). */
|
||||
Vector<SpecializationConstant::Value, 8> values;
|
||||
|
||||
void set_value(int index, uint32_t value)
|
||||
{
|
||||
BLI_assert_msg(types[index] == Type::uint_t, "Mismatch between interface and constant type");
|
||||
values[index].u = value;
|
||||
}
|
||||
|
||||
void set_value(int index, int value)
|
||||
{
|
||||
BLI_assert_msg(types[index] == Type::int_t, "Mismatch between interface and constant type");
|
||||
values[index].i = value;
|
||||
}
|
||||
|
||||
void set_value(int index, float value)
|
||||
{
|
||||
BLI_assert_msg(types[index] == Type::float_t, "Mismatch between interface and constant type");
|
||||
values[index].f = value;
|
||||
}
|
||||
|
||||
void set_value(int index, bool value)
|
||||
{
|
||||
BLI_assert_msg(types[index] == Type::bool_t, "Mismatch between interface and constant type");
|
||||
values[index].u = value ? 1 : 0;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return types.is_empty();
|
||||
}
|
||||
};
|
||||
|
||||
struct CompilationConstant {
|
||||
Type type;
|
||||
StringRefNull name;
|
||||
/* Reusing value type. */
|
||||
SpecializationConstant::Value value;
|
||||
|
||||
bool operator==(const CompilationConstant &b) const
|
||||
{
|
||||
return this->type == b.type && this->name == b.name && this->value == b.value;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gpu::shader
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_subprocess.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#if defined(WITH_OPENGL_BACKEND) && BLI_SUBPROCESS_SUPPORT
|
||||
|
||||
void GPU_compilation_subprocess_run(const char *subprocess_name);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace blender
|
||||
56
blender-5.2.0/source/blender/gpu/GPU_compute.hh
Normal file
56
blender-5.2.0/source/blender/gpu/GPU_compute.hh
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU Compute Pipeline
|
||||
*
|
||||
* Allows to dispatch compute shader tasks on the GPU.
|
||||
* Every dispatch is sent to the active `GPUContext`.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_storage_buffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Dispatch a compute shader task.
|
||||
* The number of work groups (aka thread groups) is bounded by `GPU_max_work_group_count()` which
|
||||
* might be different in each of the 3 dimensions.
|
||||
*/
|
||||
void GPU_compute_dispatch(gpu::Shader *shader,
|
||||
uint groups_x_len,
|
||||
uint groups_y_len,
|
||||
uint groups_z_len,
|
||||
const gpu::shader::SpecializationConstants *constants_state = nullptr);
|
||||
|
||||
/**
|
||||
* Dispatch a compute shader task. The size of the dispatch is sourced from a \a indirect_buf
|
||||
* which must follow this layout:
|
||||
* \code{.c}
|
||||
* struct DispatchIndirectCommand {
|
||||
* uint groups_x_len;
|
||||
* uint groups_y_len;
|
||||
* uint groups_z_len;
|
||||
* };
|
||||
* \encode
|
||||
*
|
||||
* \note The writes to the \a indirect_buf do not need to be synchronized as a memory barrier is
|
||||
* emitted internally.
|
||||
*
|
||||
* The number of work groups (aka thread groups) is bounded by `GPU_max_work_group_count()` which
|
||||
* might be different in each of the 3 dimensions.
|
||||
*/
|
||||
void GPU_compute_dispatch_indirect(
|
||||
gpu::Shader *shader,
|
||||
gpu::StorageBuf *indirect_buf,
|
||||
const gpu::shader::SpecializationConstants *constants_state = nullptr);
|
||||
|
||||
} // namespace blender
|
||||
214
blender-5.2.0/source/blender/gpu/GPU_context.hh
Normal file
214
blender-5.2.0/source/blender/gpu/GPU_context.hh
Normal file
@@ -0,0 +1,214 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* This interface allow GPU to manage VAOs for multiple context and threads.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
struct GHOST_GPUDevice;
|
||||
|
||||
class GHOST_IContext;
|
||||
class GHOST_ISystem;
|
||||
class GHOST_IWindow;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUContext;
|
||||
|
||||
/* GPU back-ends abstract the differences between different APIs. #GPU_context_create
|
||||
* automatically initializes the back-end, and #GPU_context_discard frees it when there
|
||||
* are no more contexts. */
|
||||
|
||||
bool GPU_backend_supported();
|
||||
void GPU_backend_type_selection_set(const GPUBackendType backend);
|
||||
GPUBackendType GPU_backend_type_selection_get();
|
||||
GPUBackendType GPU_backend_get_type();
|
||||
const char *GPU_backend_get_name();
|
||||
|
||||
/**
|
||||
* Detect the most suited GPUBackendType.
|
||||
*
|
||||
* - The detected backend will be set in `GPU_backend_type_selection_set`.
|
||||
* - When GPU_backend_type_selection_is_overridden it checks the overridden backend.
|
||||
* When not overridden it checks a default list.
|
||||
* - OpenGL backend will be checked as a fallback for Metal.
|
||||
*
|
||||
* Returns true when detection found a supported backend, otherwise returns false.
|
||||
* When no supported backend is found GPU_backend_type_selection_set is called with
|
||||
* GPU_BACKEND_NONE.
|
||||
*/
|
||||
bool GPU_backend_type_selection_detect();
|
||||
|
||||
/**
|
||||
* Alter the GPU_backend_type_selection_detect to only test a specific backend
|
||||
*/
|
||||
void GPU_backend_type_selection_set_override(GPUBackendType backend_type);
|
||||
|
||||
/**
|
||||
* Check if the GPU_backend_type_selection_detect is overridden to only test a specific backend.
|
||||
*/
|
||||
bool GPU_backend_type_selection_is_overridden();
|
||||
|
||||
/**
|
||||
* Override the user-preference GPU device to use the specified GPU.
|
||||
*
|
||||
* Device selection first tries the device with this (`vendor_id`, `device_id`, `index`),
|
||||
* then falls back to the preferred device unless hard fail debugging is enabled. The
|
||||
* values `vendor_id == uint32_t(-1)` and `device_id == uint32_t(-1)` are sentinels meaning
|
||||
* "wildcard" (don't constrain that field), so passing both as the sentinel selects purely by
|
||||
* enumeration `index`.
|
||||
*/
|
||||
void GPU_backend_preferred_device_set_override(int index, uint32_t vendor_id, uint32_t device_id);
|
||||
/**
|
||||
* Return the preferred GPU device for new contexts.
|
||||
*/
|
||||
GHOST_GPUDevice GPU_backend_preferred_device_get();
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
/**
|
||||
* Print one line per Vulkan device that meets minimum requirements, matching the
|
||||
* `<vendor-hex>/<device-hex>/<index>` identifier used in user preferences. Used by
|
||||
* `--gpu-device help`.
|
||||
*/
|
||||
void GPU_vulkan_supported_devices_print(FILE *fp);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the VSync value (when set).
|
||||
*/
|
||||
int GPU_backend_vsync_get();
|
||||
/**
|
||||
* Override the default VSync.
|
||||
*
|
||||
* \param vsync: See #GHOST_TVSyncModes for details.
|
||||
*/
|
||||
void GPU_backend_vsync_set_override(int vsync);
|
||||
bool GPU_backend_vsync_is_overridden();
|
||||
|
||||
/** Opaque type hiding gpu::Context. */
|
||||
GPUContext *GPU_context_create(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context);
|
||||
/**
|
||||
* To be called after #GPU_context_active_set(ctx_to_destroy).
|
||||
*/
|
||||
void GPU_context_discard(GPUContext *);
|
||||
|
||||
/**
|
||||
* #GPUContext can be null.
|
||||
*/
|
||||
void GPU_context_active_set(GPUContext *);
|
||||
GPUContext *GPU_context_active_get();
|
||||
|
||||
/**
|
||||
* Begin and end frame are used to mark the singular boundary representing the lifetime of a whole
|
||||
* frame. This also acts as a divisor for ensuring workload submission and flushing, especially for
|
||||
* background rendering when there is no call to present.
|
||||
* This is required by explicit-API's where there is no implicit workload flushing.
|
||||
*/
|
||||
void GPU_context_begin_frame(GPUContext *ctx);
|
||||
void GPU_context_end_frame(GPUContext *ctx);
|
||||
|
||||
/**
|
||||
* Legacy GPU (Intel HD4000 series) do not support sharing GPU objects between GPU
|
||||
* contexts. EEVEE/Workbench can create different contexts for image/preview rendering, baking or
|
||||
* compiling. When a legacy GPU is detected (`GPU_use_main_context_workaround()`) any worker
|
||||
* threads should use the draw manager opengl context and make sure that they are the only one
|
||||
* using it by locking the main context using these two functions.
|
||||
*/
|
||||
void GPU_context_main_lock();
|
||||
void GPU_context_main_unlock();
|
||||
|
||||
/**
|
||||
* \brief Enable shader create info pipeline state assert.
|
||||
*
|
||||
* Activates an assert when a shader create info contains pipeline states but using the shader
|
||||
* still require a new pipeline. This helps to identify mismatches between the shader create info
|
||||
* and actual usage.
|
||||
*
|
||||
* The assert can not be enabled by default as there are cases where new pipelines are expected.
|
||||
* This function is used inside unit tests to check if pipeline creation is done when not expected.
|
||||
* \param context: Context where to activate the pipeline creation debug.
|
||||
* \param enable: #true enables the feature, #false disables the feature.
|
||||
*
|
||||
* \note Currently only supported by Vulkan.
|
||||
*/
|
||||
void GPU_context_debug_pipeline_creation(GPUContext *context, bool enable);
|
||||
|
||||
/** GPU Begin/end work blocks */
|
||||
void GPU_render_begin();
|
||||
void GPU_render_end();
|
||||
|
||||
/**
|
||||
* For operations which need to run exactly once per frame -- even if there are no render updates.
|
||||
*/
|
||||
void GPU_render_step(bool force_resource_release = false);
|
||||
|
||||
/** For when we need access to a system context in order to create a GPU context. */
|
||||
void GPU_backend_ghost_system_set(GHOST_ISystem *ghost_system_handle);
|
||||
GHOST_ISystem *GPU_backend_ghost_system_get();
|
||||
|
||||
namespace gpu {
|
||||
|
||||
struct GPUSecondaryContextData {
|
||||
GHOST_IContext *ghost_context = nullptr;
|
||||
GPUContext *gpu_context = nullptr;
|
||||
};
|
||||
|
||||
/** Creates a secondary off-screen GHOST and GPU contexts. Must be called on the main thread. */
|
||||
GPUSecondaryContextData GPU_create_secondary_context();
|
||||
|
||||
/** Activates the given secondary GPU context. */
|
||||
void GPU_activate_secondary_context(const GPUSecondaryContextData &data);
|
||||
|
||||
/** Deactivates the given secondary GPU context. */
|
||||
void GPU_deactivate_secondary_context(const GPUSecondaryContextData &data);
|
||||
|
||||
/** Destroys the given secondary GPU context. */
|
||||
void GPU_destroy_secondary_context(GPUSecondaryContextData &data);
|
||||
|
||||
/**
|
||||
* Abstracts secondary GHOST and GPU context creation, activation and deletion.
|
||||
* Must be created from the main thread and destructed from the thread they where activated in.
|
||||
* (See GPUWorker for an usage example)
|
||||
*/
|
||||
class GPUSecondaryContext {
|
||||
private:
|
||||
GPUSecondaryContextData data_;
|
||||
|
||||
public:
|
||||
GPUSecondaryContext();
|
||||
~GPUSecondaryContext();
|
||||
|
||||
/** Must be called from a secondary thread. */
|
||||
void activate();
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Activate pipeline creation debugging for a certain scope.
|
||||
*/
|
||||
struct DebugScopePipelineCreation {
|
||||
private:
|
||||
GPUContext *context_ = nullptr;
|
||||
|
||||
public:
|
||||
DebugScopePipelineCreation(GPUContext *context) : context_(context)
|
||||
{
|
||||
GPU_context_debug_pipeline_creation(context, true);
|
||||
}
|
||||
~DebugScopePipelineCreation()
|
||||
{
|
||||
GPU_context_debug_pipeline_creation(context_, false);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
163
blender-5.2.0/source/blender/gpu/GPU_debug.hh
Normal file
163
blender-5.2.0/source/blender/gpu/GPU_debug.hh
Normal file
@@ -0,0 +1,163 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Helpers for GPU / drawing debugging.
|
||||
*
|
||||
*
|
||||
* GPU debug capture usage example:
|
||||
*
|
||||
* ### Instant frame capture. ###
|
||||
*
|
||||
* Will trigger a capture and load it inside RenderDoc or Xcode.
|
||||
*
|
||||
* \code
|
||||
* #include "GPU_debug.hh"
|
||||
*
|
||||
* void render_function()
|
||||
* {
|
||||
* GPU_debug_capture_begin(__func__);
|
||||
* // Draw-call submission goes here.
|
||||
* GPU_debug_capture_end();
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* ### Capture scopes. ###
|
||||
*
|
||||
* Capture scope can be sprinkled around the codebase for easier selective capture.
|
||||
*
|
||||
* They are listed from inside Xcode (on Mac) when doing a Metal capture.
|
||||
*
|
||||
* OpenGL and Vulkan backend need to use the `--debug-gpu-scope-capture` launch argument to specify
|
||||
* which scope to capture. Building with RenderDoc API support is required for this launch option
|
||||
* to be available.
|
||||
*
|
||||
* They can be nested but only one can be captured at a time.
|
||||
*
|
||||
* \code
|
||||
* #include "GPU_debug.hh"
|
||||
*
|
||||
* void render_function()
|
||||
* {
|
||||
* static gpu::DebugScope capture_scope = {"UniqueName"};
|
||||
*
|
||||
* // Manually triggered version, better for conditional capture.
|
||||
* capture_scope.begin();
|
||||
* // Draw-call submission goes here.
|
||||
* capture_scope.end();
|
||||
*
|
||||
* {
|
||||
* // Scoped version, better for complex control flow.
|
||||
* static gpu::DebugScope capture_scope = {"AnotherUniqueName"};
|
||||
* capture_scope.scoped_capture();
|
||||
* // Draw-call submission goes here.
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define GPU_DEBUG_SHADER_COMPILATION_GROUP "Shader Compilation"
|
||||
#define GPU_DEBUG_SHADER_SPECIALIZATION_GROUP "Shader Specialization"
|
||||
|
||||
void GPU_debug_group_begin(const char *name);
|
||||
void GPU_debug_group_end();
|
||||
/**
|
||||
* Return a formatted string showing the current group hierarchy in this format:
|
||||
* "Group1 > Group 2 > Group3 > ... > GroupN : "
|
||||
*/
|
||||
void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf);
|
||||
std::string GPU_debug_get_groups_names(IndexRange levels = IndexRange(0, 9999));
|
||||
/**
|
||||
* Return true if inside a debug group with the same name.
|
||||
*/
|
||||
bool GPU_debug_group_match(const char *ref);
|
||||
|
||||
/**
|
||||
* GPU Frame capture support.
|
||||
*
|
||||
* Allows instantaneous frame capture of GPU calls between begin/end.
|
||||
*
|
||||
* \param title: Optional title to set for the frame capture.
|
||||
*/
|
||||
void GPU_debug_capture_begin(const char *title);
|
||||
void GPU_debug_capture_end();
|
||||
|
||||
/**
|
||||
* GPU debug frame capture scopes.
|
||||
*
|
||||
* Allows creation of a GPU frame capture scope that define a region within which an external GPU
|
||||
* Frame capture tool can perform a deferred capture of GPU API calls within the boundary upon user
|
||||
* request.
|
||||
*
|
||||
* \param name: Unique name of capture scope displayed within capture tool.
|
||||
* \return pointer wrapping an API-specific capture scope object.
|
||||
* \note a capture scope should be created a single time and only used within one begin/end pair.
|
||||
*/
|
||||
void *GPU_debug_capture_scope_create(const char *name);
|
||||
|
||||
/**
|
||||
* Used to declare the region within which GPU calls are captured when the scope is triggered.
|
||||
*
|
||||
* \param scope: Pointer to capture scope object created with GPU_debug_capture_scope_create.
|
||||
* \return True if the capture tool is actively capturing this scope when function is executed.
|
||||
* Otherwise, False.
|
||||
*/
|
||||
bool GPU_debug_capture_scope_begin(void *scope);
|
||||
void GPU_debug_capture_scope_end(void *scope);
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/**
|
||||
* Need to be declared as static with a unique identifier string.
|
||||
*/
|
||||
struct DebugScope {
|
||||
void *scope;
|
||||
|
||||
DebugScope(const char *identifier)
|
||||
{
|
||||
scope = GPU_debug_capture_scope_create(identifier);
|
||||
}
|
||||
|
||||
void begin_capture()
|
||||
{
|
||||
GPU_debug_capture_scope_begin(scope);
|
||||
}
|
||||
|
||||
void end_capture()
|
||||
{
|
||||
GPU_debug_capture_scope_end(scope);
|
||||
}
|
||||
|
||||
struct ScopedCapture {
|
||||
void *scope;
|
||||
|
||||
ScopedCapture(void *scope) : scope(scope)
|
||||
{
|
||||
GPU_debug_capture_scope_begin(scope);
|
||||
}
|
||||
~ScopedCapture()
|
||||
{
|
||||
GPU_debug_capture_scope_end(scope);
|
||||
}
|
||||
};
|
||||
|
||||
/* Capture everything until the end of the scope. */
|
||||
ScopedCapture scoped_capture()
|
||||
{
|
||||
return ScopedCapture(scope);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
265
blender-5.2.0/source/blender/gpu/GPU_format.hh
Normal file
265
blender-5.2.0/source/blender/gpu/GPU_format.hh
Normal file
@@ -0,0 +1,265 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Standard Formats
|
||||
* \{ */
|
||||
|
||||
/* NOTE: Metal does not support pixel formats with 3 channel. These are aliased to 4 channel types
|
||||
* and converted on data upload. */
|
||||
/* clang-format off */
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SNORM_8_(impl) impl(/*TODO*/, 1 * 1, 1, SNORM_8, R8_SNORM, R8Snorm, CharNormalized, R8_SNORM, r8_snorm)
|
||||
#define SNORM_8_8_(impl) impl(/*TODO*/, 1 * 2, 2, SNORM_8_8, R8G8_SNORM, RG8Snorm, Char2Normalized, RG8_SNORM, rg8_snorm)
|
||||
#define SNORM_8_8_8_(impl) impl(/*TODO*/, 1 * 3, 3, SNORM_8_8_8, R8G8B8_SNORM, RGBA8Snorm, Char3Normalized, RGB8_SNORM, rgb8_snorm)
|
||||
#define SNORM_8_8_8_8_(impl) impl(/*TODO*/, 1 * 4, 4, SNORM_8_8_8_8, R8G8B8A8_SNORM, RGBA8Snorm, Char4Normalized, RGBA8_SNORM, rgba8_snorm)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SNORM_16_(impl) impl(/*TODO*/, 2 * 1, 1, SNORM_16, R16_SNORM, R16Snorm, ShortNormalized, R16_SNORM, r16_snorm)
|
||||
#define SNORM_16_16_(impl) impl(/*TODO*/, 2 * 2, 2, SNORM_16_16, R16G16_SNORM, RG16Snorm, Short2Normalized, RG16_SNORM, rg16_snorm)
|
||||
#define SNORM_16_16_16_(impl) impl(/*TODO*/, 2 * 3, 3, SNORM_16_16_16, R16G16B16_SNORM, RGBA16Snorm, Short3Normalized, RGB16_SNORM, rgb16_snorm)
|
||||
#define SNORM_16_16_16_16_(impl) impl(/*TODO*/, 2 * 4, 4, SNORM_16_16_16_16, R16G16B16A16_SNORM, RGBA16Snorm, Short4Normalized, RGBA16_SNORM, rgba16_snorm)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define UNORM_8_(impl) impl(/*TODO*/, 1 * 1, 1, UNORM_8, R8_UNORM, R8Unorm, UCharNormalized, R8, r8_unorm)
|
||||
#define UNORM_8_8_(impl) impl(/*TODO*/, 1 * 2, 2, UNORM_8_8, R8G8_UNORM, RG8Unorm, UChar2Normalized, RG8, rg8_unorm)
|
||||
#define UNORM_8_8_8_(impl) impl(/*TODO*/, 1 * 3, 3, UNORM_8_8_8, R8G8B8_UNORM, RGBA8Unorm, UChar3Normalized, RGB8, rgb8_unorm)
|
||||
#define UNORM_8_8_8_8_(impl) impl(/*TODO*/, 1 * 4, 4, UNORM_8_8_8_8, R8G8B8A8_UNORM, RGBA8Unorm, UChar4Normalized, RGBA8, rgba8_unorm)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define UNORM_16_(impl) impl(/*TODO*/, 2 * 1, 1, UNORM_16, R16_UNORM, R16Unorm, UShortNormalized, R16, r16_unorm)
|
||||
#define UNORM_16_16_(impl) impl(/*TODO*/, 2 * 2, 2, UNORM_16_16, R16G16_UNORM, RG16Unorm, UShort2Normalized, RG16, rg16_unorm)
|
||||
#define UNORM_16_16_16_(impl) impl(/*TODO*/, 2 * 3, 3, UNORM_16_16_16, R16G16B16_UNORM, RGBA16Unorm, UShort3Normalized, RGB16, rgb16_unorm)
|
||||
#define UNORM_16_16_16_16_(impl) impl(/*TODO*/, 2 * 4, 4, UNORM_16_16_16_16, R16G16B16A16_UNORM, RGBA16Unorm, UShort4Normalized, RGBA16, rgba16_unorm)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SINT_8_(impl) impl(int8_t, 1 * 1, 1, SINT_8, R8_SINT, R8Sint, Char, R8I, r8_sint)
|
||||
#define SINT_8_8_(impl) impl(char2, 1 * 2, 2, SINT_8_8, R8G8_SINT, RG8Sint, Char2, RG8I, rg8_sint)
|
||||
#define SINT_8_8_8_(impl) impl(char3, 1 * 3, 3, SINT_8_8_8, R8G8B8_SINT, RGBA8Sint, Char3, RGB8I, rgb8_sint)
|
||||
#define SINT_8_8_8_8_(impl) impl(char4, 1 * 4, 4, SINT_8_8_8_8, R8G8B8A8_SINT, RGBA8Sint, Char4, RGBA8I, rgba8_sint)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SINT_16_(impl) impl(int16_t, 2 * 1, 1, SINT_16, R16_SINT, R16Sint, Short, R16I, r16_sint)
|
||||
#define SINT_16_16_(impl) impl(short2, 2 * 2, 2, SINT_16_16, R16G16_SINT, RG16Sint, Short2, RG16I, rg16_sint)
|
||||
#define SINT_16_16_16_(impl) impl(short3, 2 * 3, 3, SINT_16_16_16, R16G16B16_SINT, RGBA16Sint, Short3, RGB16I, rgb16_sint)
|
||||
#define SINT_16_16_16_16_(impl) impl(short4, 2 * 4, 4, SINT_16_16_16_16, R16G16B16A16_SINT, RGBA16Sint, Short4, RGBA16I, rgba16_sint)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SINT_32_(impl) impl(int32_t, 4 * 1, 1, SINT_32, R32_SINT, R32Sint, Int, R32I, r32_sint)
|
||||
#define SINT_32_32_(impl) impl(int2, 4 * 2, 2, SINT_32_32, R32G32_SINT, RG32Sint, Int2, RG32I, rg32_sint)
|
||||
#define SINT_32_32_32_(impl) impl(int3, 4 * 3, 3, SINT_32_32_32, R32G32B32_SINT, RGBA32Sint, Int3, RGB32I, rgb32_sint)
|
||||
#define SINT_32_32_32_32_(impl) impl(int4, 4 * 4, 4, SINT_32_32_32_32, R32G32B32A32_SINT, RGBA32Sint, Int4, RGBA32I, rgba32_sint)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define UINT_8_(impl) impl(uint8_t, 1 * 1, 1, UINT_8, R8_UINT, R8Uint, UChar, R8UI, r8_uint)
|
||||
#define UINT_8_8_(impl) impl(uchar2, 1 * 2, 2, UINT_8_8, R8G8_UINT, RG8Uint, UChar2, RG8UI, rg8_uint)
|
||||
#define UINT_8_8_8_(impl) impl(uchar3, 1 * 3, 3, UINT_8_8_8, R8G8B8_UINT, RGBA8Uint, UChar3, RGB8UI, rgb8_uint)
|
||||
#define UINT_8_8_8_8_(impl) impl(uchar4, 1 * 4, 4, UINT_8_8_8_8, R8G8B8A8_UINT, RGBA8Uint, UChar4, RGBA8UI, rgba8_uint)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define UINT_16_(impl) impl(uint16_t, 2 * 1, 1, UINT_16, R16_UINT, R16Uint, UShort, R16UI, r16_uint)
|
||||
#define UINT_16_16_(impl) impl(ushort2, 2 * 2, 2, UINT_16_16, R16G16_UINT, RG16Uint, UShort2, RG16UI, rg16_uint)
|
||||
#define UINT_16_16_16_(impl) impl(ushort3, 2 * 3, 3, UINT_16_16_16, R16G16B16_UINT, RGBA16Uint, UShort3, RGB16UI, rgb16_uint)
|
||||
#define UINT_16_16_16_16_(impl) impl(ushort4, 2 * 4, 4, UINT_16_16_16_16, R16G16B16A16_UINT, RGBA16Uint, UShort4, RGBA16UI, rgba16_uint)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define UINT_32_(impl) impl(uint32_t, 4 * 1, 1, UINT_32, R32_UINT, R32Uint, UInt, R32UI, r32_uint)
|
||||
#define UINT_32_32_(impl) impl(uint2, 4 * 2, 2, UINT_32_32, R32G32_UINT, RG32Uint, UInt2, RG32UI, rg32_uint)
|
||||
#define UINT_32_32_32_(impl) impl(uint3, 4 * 3, 3, UINT_32_32_32, R32G32B32_UINT, RGBA32Uint, UInt3, RGB32UI, rgb32_uint)
|
||||
#define UINT_32_32_32_32_(impl) impl(uint4, 4 * 4, 4, UINT_32_32_32_32, R32G32B32A32_UINT, RGBA32Uint, UInt4, RGBA32UI, rgba32_uint)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SFLOAT_16_(impl) impl(/*TODO*/, 2 * 1, 1, SFLOAT_16, R16_SFLOAT, R16Float, Half, R16F, r16_sfloat)
|
||||
#define SFLOAT_16_16_(impl) impl(/*TODO*/, 2 * 2, 2, SFLOAT_16_16, R16G16_SFLOAT, RG16Float, Half2, RG16F, rg16_sfloat)
|
||||
#define SFLOAT_16_16_16_(impl) impl(/*TODO*/, 2 * 3, 3, SFLOAT_16_16_16, R16G16B16_SFLOAT, RGBA16Float, Half3, RGB16F, rgb16_sfloat)
|
||||
#define SFLOAT_16_16_16_16_(impl) impl(/*TODO*/, 2 * 4, 4, SFLOAT_16_16_16_16, R16G16B16A16_SFLOAT, RGBA16Float, Half4, RGBA16F, rgba16_sfloat)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SFLOAT_32_(impl) impl(float, 4 * 1, 1, SFLOAT_32, R32_SFLOAT, R32Float, Float, R32F, r32_sfloat)
|
||||
#define SFLOAT_32_32_(impl) impl(float2, 4 * 2, 2, SFLOAT_32_32, R32G32_SFLOAT, RG32Float, Float2, RG32F, rg32_sfloat)
|
||||
#define SFLOAT_32_32_32_(impl) impl(float3, 4 * 3, 3, SFLOAT_32_32_32, R32G32B32_SFLOAT, RGBA32Float, Float3, RGB32F, rgb32_sfloat)
|
||||
#define SFLOAT_32_32_32_32_(impl) impl(float4, 4 * 4, 4, SFLOAT_32_32_32_32, R32G32B32A32_SFLOAT, RGBA32Float, Float4, RGBA32F, rgba32_sfloat)
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Special Formats
|
||||
* \{ */
|
||||
|
||||
/* clang-format off */
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SNORM_10_10_10_2_(impl) impl(/*TODO*/, 4, 4, SNORM_10_10_10_2, A2B10G10R10_SNORM_PACK32, /* n/a */, Int1010102Normalized, /* n/a */, /* n/a */ )
|
||||
#define UNORM_10_10_10_2_(impl) impl(/*TODO*/, 4, 4, UNORM_10_10_10_2, A2B10G10R10_UNORM_PACK32, RGB10A2Unorm, UInt1010102Normalized, RGB10_A2, rgb10_a2_unorm )
|
||||
#define UINT_10_10_10_2_(impl) impl(/*TODO*/, 4, 4, UINT_10_10_10_2, A2B10G10R10_UINT_PACK32, RGB10A2Uint, /* n/a */, RGB10_A2UI, rgb10_a2_uint )
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define UFLOAT_11_11_10_(impl) impl(/*TODO*/, 4, 3, UFLOAT_11_11_10, B10G11R11_UFLOAT_PACK32, RG11B10Float, FloatRG11B10, R11F_G11F_B10F, r11_g11_b10_ufloat)
|
||||
#define UFLOAT_9_9_9_EXP_5_(impl) impl(/*TODO*/, 4, 3, UFLOAT_9_9_9_EXP_5, E5B9G9R9_UFLOAT_PACK32, RGB9E5Float, FloatRGB9E5, RGB9_E5, /* n/a */)
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SRGBA_8_8_8_8_(impl) impl(/*TODO*/, 4, 4, SRGBA_8_8_8_8, R8G8B8A8_SRGB, RGBA8Unorm_sRGB, /* n/a */, SRGB8_ALPHA8, /* n/a */ )
|
||||
#define SRGBA_8_8_8_(impl) impl(/*TODO*/, 3, 3, SRGBA_8_8_8, R8G8B8_SRGB, RGBA8Unorm_sRGB, /* n/a */, SRGB8, /* n/a */ )
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define UNORM_16_DEPTH_(impl) impl(/*TODO*/, 4, 1, UNORM_16_DEPTH, D16_UNORM, Depth16Unorm, /* n/a */, DEPTH_COMPONENT16, /* n/a */ )
|
||||
#define UNORM_24_DEPTH_(impl) impl(/*TODO*/, 4, 1, UNORM_24_DEPTH, X8_D24_UNORM_PACK32, Depth24Unorm_Stencil8, /* n/a */, DEPTH_COMPONENT24, /* n/a */ )
|
||||
#define UNORM_24_DEPTH_UINT_8_(impl) impl(/*TODO*/, 8, 1, UNORM_24_DEPTH_UINT_8, D24_UNORM_S8_UINT, Depth24Unorm_Stencil8, /* n/a */, DEPTH24_STENCIL8, /* n/a */ )
|
||||
#define SFLOAT_32_DEPTH_(impl) impl(/*TODO*/, 4, 1, SFLOAT_32_DEPTH, D32_SFLOAT, Depth32Float, /* n/a */, DEPTH_COMPONENT32F, /* n/a */ )
|
||||
#define SFLOAT_32_DEPTH_UINT_8_(impl) impl(/*TODO*/, 8, 1, SFLOAT_32_DEPTH_UINT_8, D32_SFLOAT_S8_UINT, Depth32Float_Stencil8, /* n/a */, DEPTH32F_STENCIL8, /* n/a */ )
|
||||
/* type size comps blender_enum vk_enum mtl_pixel_enum mtl_vertex_enum gl_pixel_enum shader_enum */
|
||||
#define SNORM_DXT1_(impl) impl(/* n/a */, 1, 1, SNORM_DXT1, BC1_RGBA_UNORM_BLOCK, BC1_RGBA, /* n/a */, COMPRESSED_RGBA_S3TC_DXT1_EXT, /* n/a */ )
|
||||
#define SNORM_DXT3_(impl) impl(/* n/a */, 1, 1, SNORM_DXT3, BC2_UNORM_BLOCK, BC2_RGBA, /* n/a */, COMPRESSED_RGBA_S3TC_DXT3_EXT, /* n/a */ )
|
||||
#define SNORM_DXT5_(impl) impl(/* n/a */, 1, 1, SNORM_DXT5, BC3_UNORM_BLOCK, BC3_RGBA, /* n/a */, COMPRESSED_RGBA_S3TC_DXT5_EXT, /* n/a */ )
|
||||
#define SRGB_DXT1_(impl) impl(/* n/a */, 1, 1, SRGB_DXT1, BC1_RGBA_SRGB_BLOCK, BC1_RGBA_sRGB, /* n/a */, COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, /* n/a */ )
|
||||
#define SRGB_DXT3_(impl) impl(/* n/a */, 1, 1, SRGB_DXT3, BC2_SRGB_BLOCK, BC2_RGBA_sRGB, /* n/a */, COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, /* n/a */ )
|
||||
#define SRGB_DXT5_(impl) impl(/* n/a */, 1, 1, SRGB_DXT5, BC3_SRGB_BLOCK, BC3_RGBA_sRGB, /* n/a */, COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, /* n/a */ )
|
||||
/* clang-format on */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Data Formats
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Collection of all data formats.
|
||||
* Vertex formats and Texture formats are both subset of this.
|
||||
*/
|
||||
enum class DataFormat : uint8_t {
|
||||
Invalid = 0,
|
||||
|
||||
#define DECLARE(a, b, c, blender_enum, d, e, f, g, h) blender_enum,
|
||||
|
||||
#define GPU_DATA_FORMAT_EXPAND(impl) \
|
||||
SNORM_8_(impl) \
|
||||
SNORM_8_8_(impl) \
|
||||
SNORM_8_8_8_(impl) \
|
||||
SNORM_8_8_8_8_(impl) \
|
||||
\
|
||||
SNORM_16_(impl) \
|
||||
SNORM_16_16_(impl) \
|
||||
SNORM_16_16_16_(impl) \
|
||||
SNORM_16_16_16_16_(impl) \
|
||||
\
|
||||
UNORM_8_(impl) \
|
||||
UNORM_8_8_(impl) \
|
||||
UNORM_8_8_8_(impl) \
|
||||
UNORM_8_8_8_8_(impl) \
|
||||
\
|
||||
UNORM_16_(impl) \
|
||||
UNORM_16_16_(impl) \
|
||||
UNORM_16_16_16_(impl) \
|
||||
UNORM_16_16_16_16_(impl) \
|
||||
\
|
||||
SINT_8_(impl) \
|
||||
SINT_8_8_(impl) \
|
||||
SINT_8_8_8_(impl) \
|
||||
SINT_8_8_8_8_(impl) \
|
||||
\
|
||||
SINT_16_(impl) \
|
||||
SINT_16_16_(impl) \
|
||||
SINT_16_16_16_(impl) \
|
||||
SINT_16_16_16_16_(impl) \
|
||||
\
|
||||
SINT_32_(impl) \
|
||||
SINT_32_32_(impl) \
|
||||
SINT_32_32_32_(impl) \
|
||||
SINT_32_32_32_32_(impl) \
|
||||
\
|
||||
UINT_8_(impl) \
|
||||
UINT_8_8_(impl) \
|
||||
UINT_8_8_8_(impl) \
|
||||
UINT_8_8_8_8_(impl) \
|
||||
\
|
||||
UINT_16_(impl) \
|
||||
UINT_16_16_(impl) \
|
||||
UINT_16_16_16_(impl) \
|
||||
UINT_16_16_16_16_(impl) \
|
||||
\
|
||||
UINT_32_(impl) \
|
||||
UINT_32_32_(impl) \
|
||||
UINT_32_32_32_(impl) \
|
||||
UINT_32_32_32_32_(impl) \
|
||||
\
|
||||
SFLOAT_16_(impl) \
|
||||
SFLOAT_16_16_(impl) \
|
||||
SFLOAT_16_16_16_(impl) \
|
||||
SFLOAT_16_16_16_16_(impl) \
|
||||
\
|
||||
SFLOAT_32_(impl) \
|
||||
SFLOAT_32_32_(impl) \
|
||||
SFLOAT_32_32_32_(impl) \
|
||||
SFLOAT_32_32_32_32_(impl) \
|
||||
\
|
||||
SNORM_10_10_10_2_(impl) \
|
||||
UNORM_10_10_10_2_(impl) \
|
||||
UINT_10_10_10_2_(impl) \
|
||||
\
|
||||
UFLOAT_11_11_10_(impl) \
|
||||
UFLOAT_9_9_9_EXP_5_(impl) \
|
||||
\
|
||||
UNORM_16_DEPTH_(impl) \
|
||||
UNORM_24_DEPTH_(impl) /* TODO(fclem): Incompatible with metal, is emulated. To remove. */ \
|
||||
UNORM_24_DEPTH_UINT_8_(impl) \
|
||||
SFLOAT_32_DEPTH_(impl) \
|
||||
SFLOAT_32_DEPTH_UINT_8_(impl) \
|
||||
\
|
||||
SRGBA_8_8_8_(impl) \
|
||||
SRGBA_8_8_8_8_(impl) \
|
||||
\
|
||||
SNORM_DXT1_(impl) \
|
||||
SNORM_DXT3_(impl) \
|
||||
SNORM_DXT5_(impl) \
|
||||
SRGB_DXT1_(impl) \
|
||||
SRGB_DXT3_(impl) \
|
||||
SRGB_DXT5_(impl)
|
||||
|
||||
GPU_DATA_FORMAT_EXPAND(DECLARE)
|
||||
|
||||
#undef DECLARE
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
*
|
||||
* Allow querying information about the format enum values.
|
||||
* \{ */
|
||||
|
||||
/* NOTE: Compressed format bytesize are rounded up as their actual value is fractional. */
|
||||
inline int to_bytesize(const DataFormat format)
|
||||
{
|
||||
#define CASE(a, size, c, blender_enum, d, e, f, g, h) \
|
||||
case DataFormat::blender_enum: \
|
||||
return size;
|
||||
|
||||
switch (format) {
|
||||
GPU_DATA_FORMAT_EXPAND(CASE)
|
||||
case DataFormat::Invalid:
|
||||
break;
|
||||
}
|
||||
#undef CASE
|
||||
BLI_assert_unreachable();
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline int format_component_len(const DataFormat format)
|
||||
{
|
||||
#define CASE(a, b, comp, blender_enum, d, e, f, g, h) \
|
||||
case DataFormat::blender_enum: \
|
||||
return comp;
|
||||
|
||||
switch (format) {
|
||||
GPU_DATA_FORMAT_EXPAND(CASE)
|
||||
case DataFormat::Invalid:
|
||||
break;
|
||||
}
|
||||
#undef CASE
|
||||
BLI_assert_unreachable();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
730
blender-5.2.0/source/blender/gpu/GPU_framebuffer.hh
Normal file
730
blender-5.2.0/source/blender/gpu/GPU_framebuffer.hh
Normal file
@@ -0,0 +1,730 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* A `GPUFramebuffer` is a wrapper for a frame-buffer object (FBO) from the underlying graphic API.
|
||||
*
|
||||
* A `GPUFramebuffer` is limited to one context and thus cannot be shared across different
|
||||
* contexts. In the case this is needed, one must recreate the same `GPUFramebuffer` in each
|
||||
* context.
|
||||
*
|
||||
* Note that actual FBO creation & config is deferred until `GPU_framebuffer_bind` or
|
||||
* `GPU_framebuffer_check_valid` is called. This means the context the `GPUFramebuffer` is bound
|
||||
* with is the one active when `GPU_framebuffer_bind` is called.
|
||||
*
|
||||
* When a `gpu::Texture` is attached to a `GPUFramebuffer` a reference is created.
|
||||
* Deleting either does not require any unbinding.
|
||||
*
|
||||
* A `GPUOffScreen` is a convenience type that holds a `GPUFramebuffer` and its associated
|
||||
* `gpu::Texture`s. It is useful for quick drawing surface configuration.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_common_types.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
class FrameBuffer;
|
||||
} // namespace gpu
|
||||
|
||||
enum GPUFrameBufferBits {
|
||||
GPU_COLOR_BIT = (1 << 0),
|
||||
GPU_DEPTH_BIT = (1 << 1),
|
||||
GPU_STENCIL_BIT = (1 << 2),
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUFrameBufferBits)
|
||||
|
||||
/* Guaranteed by the spec and is never greater than 16 on any hardware or implementation. */
|
||||
constexpr static int GPU_MAX_VIEWPORTS = 16;
|
||||
|
||||
struct GPUAttachment {
|
||||
gpu::Texture *tex;
|
||||
int layer, mip;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Create a #FrameBuffer object. It is not configured and not bound to a specific context until
|
||||
* `GPU_framebuffer_bind()` is called.
|
||||
*/
|
||||
gpu::FrameBuffer *GPU_framebuffer_create(const char *name);
|
||||
|
||||
/**
|
||||
* Returns the current context active framebuffer.
|
||||
* Return nullptr if no context is active.
|
||||
*/
|
||||
gpu::FrameBuffer *GPU_framebuffer_active_get();
|
||||
|
||||
/**
|
||||
* Returns the default (back-left) frame-buffer. It will always exists even if it's just a dummy.
|
||||
* Return nullptr if no context is active.
|
||||
*/
|
||||
gpu::FrameBuffer *GPU_framebuffer_back_get();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Free
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Create a #FrameBuffer object. It is not configured and not bound to a specific context until
|
||||
* `GPU_framebuffer_bind()` is called.
|
||||
*/
|
||||
void GPU_framebuffer_free(gpu::FrameBuffer *fb);
|
||||
|
||||
#define GPU_FRAMEBUFFER_FREE_SAFE(fb) \
|
||||
do { \
|
||||
if (fb != nullptr) { \
|
||||
GPU_framebuffer_free(fb); \
|
||||
fb = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Binding
|
||||
* \{ */
|
||||
|
||||
enum GPUBackBuffer {
|
||||
/** Default framebuffer of a window. Always available. */
|
||||
GPU_BACKBUFFER_LEFT = 0,
|
||||
/** Right buffer of a window. Only available if window was created using stereo-view. */
|
||||
GPU_BACKBUFFER_RIGHT,
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds the active context's window frame-buffer.
|
||||
* Note that `GPU_BACKBUFFER_RIGHT` is only available if the window was created using stereo-view.
|
||||
*/
|
||||
void GPU_backbuffer_bind(GPUBackBuffer back_buffer_type);
|
||||
|
||||
/**
|
||||
* Binds a #FrameBuffer making it the active framebuffer for all geometry rendering.
|
||||
*/
|
||||
void GPU_framebuffer_bind(gpu::FrameBuffer *fb);
|
||||
|
||||
/**
|
||||
* Same as `GPU_framebuffer_bind` but do not enable the SRGB transform.
|
||||
*/
|
||||
void GPU_framebuffer_bind_no_srgb(gpu::FrameBuffer *fb);
|
||||
|
||||
/**
|
||||
* Binds back the active context's default frame-buffer.
|
||||
* Equivalent to `GPU_backbuffer_bind(GPU_BACKBUFFER_LEFT)`.
|
||||
*/
|
||||
void GPU_framebuffer_restore();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Advanced binding control
|
||||
* \{ */
|
||||
|
||||
struct GPULoadStore {
|
||||
GPULoadOp load_action;
|
||||
GPUStoreOp store_action;
|
||||
double clear_value[4];
|
||||
};
|
||||
|
||||
/* Empty bind point. */
|
||||
#define NULL_ATTACHMENT_COLOR {0.0, 0.0, 0.0, 0.0}
|
||||
#define NULL_LOAD_STORE \
|
||||
{GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_DONT_CARE, NULL_ATTACHMENT_COLOR}
|
||||
|
||||
/**
|
||||
* Load store config array (load_store_actions) matches attachment structure of
|
||||
* GPU_framebuffer_config_array. This allows us to explicitly specify whether attachment data needs
|
||||
* to be loaded and stored on a per-attachment basis. This enables a number of bandwidth
|
||||
* optimizations:
|
||||
* - No need to load contents if subsequent work is over-writing every pixel.
|
||||
* - No need to store attachments whose contents are not used beyond this pass e.g. depth buffer.
|
||||
* - State can be customized at bind-time rather than applying to the frame-buffer object as a
|
||||
* whole.
|
||||
*
|
||||
* NOTE: Using GPU_framebuffer_clear_* functions in conjunction with a custom load-store
|
||||
* configuration is invalid. Instead, utilize GPU_LOADACTION_CLEAR and provide a clear color as
|
||||
* the third parameter in `GPULoadStore action`.
|
||||
*
|
||||
* For Color attachments: `{GPU_LOADACTION_CLEAR, GPU_STOREACTION_STORE, {Rf, Gf, Bf, Af}}`
|
||||
* For Depth attachments: `{GPU_LOADACTION_CLEAR, GPU_STOREACTION_STORE, {Df}}`
|
||||
*
|
||||
* Example:
|
||||
* \code{.c}
|
||||
* GPU_framebuffer_bind_loadstore(&fb, {
|
||||
* {GPU_LOADACTION_LOAD, GPU_STOREACTION_DONT_CARE} // must be depth buffer
|
||||
* {GPU_LOADACTION_LOAD, GPU_STOREACTION_STORE}, // Color attachment 0
|
||||
* {GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, // Color attachment 1
|
||||
* {GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE} // Color attachment 2
|
||||
* }, 4)
|
||||
* \endcode
|
||||
*/
|
||||
void GPU_framebuffer_bind_loadstore(gpu::FrameBuffer *fb,
|
||||
const GPULoadStore *load_store_actions,
|
||||
uint load_store_actions_len);
|
||||
#define GPU_framebuffer_bind_ex(_fb, ...) \
|
||||
{ \
|
||||
GPULoadStore actions[] = __VA_ARGS__; \
|
||||
GPU_framebuffer_bind_loadstore(_fb, actions, (sizeof(actions) / sizeof(GPULoadStore))); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-pass config array matches attachment structure of `GPU_framebuffer_config_array`.
|
||||
* This allows to explicitly specify attachment state within the next sub-pass.
|
||||
* This enables a number of bandwidth optimizations specially on Tile Based Deferred Renderers
|
||||
* where the attachments can be kept into tile memory and used in place for later sub-passes.
|
||||
*
|
||||
* IMPORTANT: When using this, the framebuffer initial state is undefined. A sub-pass transition
|
||||
* need to be issued before any draw-call.
|
||||
*
|
||||
* Example:
|
||||
* \code{.c}
|
||||
* GPU_framebuffer_bind_loadstore(&fb, {
|
||||
* GPU_ATTACHMENT_WRITE, // must be depth buffer
|
||||
* GPU_ATTACHMENT_READ, // Color attachment 0
|
||||
* GPU_ATTACHMENT_IGNORE, // Color attachment 1
|
||||
* GPU_ATTACHMENT_WRITE} // Color attachment 2
|
||||
* })
|
||||
* \endcode
|
||||
*
|
||||
* \note Excess attachments will have no effect as long as they are GPU_ATTACHMENT_IGNORE.
|
||||
*/
|
||||
void GPU_framebuffer_subpass_transition_array(gpu::FrameBuffer *fb,
|
||||
const GPUAttachmentState *attachment_states,
|
||||
uint attachment_len);
|
||||
|
||||
#define GPU_framebuffer_subpass_transition(_fb, ...) \
|
||||
{ \
|
||||
GPUAttachmentState actions[] = __VA_ARGS__; \
|
||||
GPU_framebuffer_subpass_transition_array( \
|
||||
_fb, actions, (sizeof(actions) / sizeof(GPUAttachmentState))); \
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Attachments
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* How to use #GPU_framebuffer_ensure_config().
|
||||
*
|
||||
* Example:
|
||||
* \code{.c}
|
||||
* GPU_framebuffer_ensure_config(&fb, {
|
||||
* GPU_ATTACHMENT_TEXTURE(depth), // must be depth buffer
|
||||
* GPU_ATTACHMENT_TEXTURE(tex1),
|
||||
* GPU_ATTACHMENT_TEXTURE_CUBEFACE(tex2, 0),
|
||||
* GPU_ATTACHMENT_TEXTURE_LAYER_MIP(tex2, 0, 0)
|
||||
* })
|
||||
* \endcode
|
||||
*
|
||||
* \note Unspecified attachments (i.e: those beyond the last
|
||||
* GPU_ATTACHMENT_* in GPU_framebuffer_ensure_config list) are left unchanged.
|
||||
*
|
||||
* \note Make sure that the dimensions of your textures matches
|
||||
* otherwise you will have an invalid framebuffer error.
|
||||
*/
|
||||
#define GPU_framebuffer_ensure_config(_fb, ...) \
|
||||
do { \
|
||||
if (*(_fb) == nullptr) { \
|
||||
*(_fb) = GPU_framebuffer_create(#_fb); \
|
||||
} \
|
||||
GPUAttachment config[] = __VA_ARGS__; \
|
||||
GPU_framebuffer_config_array(*(_fb), config, (sizeof(config) / sizeof(GPUAttachment))); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* First #GPUAttachment in *config is always the depth/depth_stencil buffer.
|
||||
* Following #GPUAttachments are color buffers.
|
||||
* Setting #GPUAttachment.mip to -1 will leave the texture in this slot.
|
||||
* Setting #GPUAttachment.tex to nullptr will detach the texture in this slot.
|
||||
*/
|
||||
void GPU_framebuffer_config_array(gpu::FrameBuffer *fb,
|
||||
const GPUAttachment *config,
|
||||
int config_len);
|
||||
|
||||
/** Empty bind point. */
|
||||
#define GPU_ATTACHMENT_NONE \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
nullptr, -1, 0, \
|
||||
}
|
||||
/** Leave currently bound texture in this slot. DEPRECATED: Specify all textures for clarity. */
|
||||
#define GPU_ATTACHMENT_LEAVE \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
nullptr, -1, -1, \
|
||||
}
|
||||
/** Bind the first mip level of a texture (all layers). */
|
||||
#define GPU_ATTACHMENT_TEXTURE(_texture) \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
_texture, -1, 0, \
|
||||
}
|
||||
/** Bind the \a _mip level of a texture (all layers). */
|
||||
#define GPU_ATTACHMENT_TEXTURE_MIP(_texture, _mip) \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
_texture, -1, _mip, \
|
||||
}
|
||||
/** Bind the \a _layer layer of the first mip level of a texture. */
|
||||
#define GPU_ATTACHMENT_TEXTURE_LAYER(_texture, _layer) \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
_texture, _layer, 0, \
|
||||
}
|
||||
/** Bind the \a _layer layer of the \a _mip level of a texture. */
|
||||
#define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_texture, _layer, _mip) \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
_texture, _layer, _mip, \
|
||||
}
|
||||
|
||||
/** NOTE: The cube-face variants are equivalent to the layer ones but give better semantic. */
|
||||
|
||||
/** Bind the first mip level of a cube-map \a _face texture. */
|
||||
#define GPU_ATTACHMENT_TEXTURE_CUBEFACE(_texture, _face) \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
_texture, _face, 0, \
|
||||
}
|
||||
/** Bind the \a _mip level of a cube-map \a _face texture. */
|
||||
#define GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(_texture, _face, _mip) \
|
||||
GPUAttachment \
|
||||
{ \
|
||||
_texture, _face, _mip, \
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach an entire texture mip level to a #FrameBuffer.
|
||||
* Changes will only take effect next time `GPU_framebuffer_bind()` is called.
|
||||
* \a slot is the color texture slot to bind this texture to. Must be 0 if it is the depth texture.
|
||||
* \a mip is the mip level of this texture to attach to the framebuffer.
|
||||
* DEPRECATED: Prefer using multiple #FrameBuffer with different configurations with
|
||||
* `GPU_framebuffer_config_array()`.
|
||||
*/
|
||||
void GPU_framebuffer_texture_attach(gpu::FrameBuffer *fb,
|
||||
gpu::Texture *texture,
|
||||
int slot,
|
||||
int mip);
|
||||
|
||||
/**
|
||||
* Attach a single layer of an array texture mip level to a #FrameBuffer.
|
||||
* Changes will only take effect next time `GPU_framebuffer_bind()` is called.
|
||||
* \a slot is the color texture slot to bind this texture to. Must be 0 if it is the depth texture.
|
||||
* \a layer is the layer of this array texture to attach to the framebuffer.
|
||||
* \a mip is the mip level of this texture to attach to the framebuffer.
|
||||
* DEPRECATED: Prefer using multiple #FrameBuffer with different configurations with
|
||||
* `GPU_framebuffer_config_array()`.
|
||||
*/
|
||||
void GPU_framebuffer_texture_layer_attach(
|
||||
gpu::FrameBuffer *fb, gpu::Texture *texture, int slot, int layer, int mip);
|
||||
|
||||
/**
|
||||
* Attach a single cube-face of an cube-map texture mip level to a #FrameBuffer.
|
||||
* Changes will only take effect next time `GPU_framebuffer_bind()` is called.
|
||||
* \a slot is the color texture slot to bind this texture to. Must be 0 if it is the depth texture.
|
||||
* \a face is the cube-face of this cube-map texture to attach to the framebuffer.
|
||||
* \a mip is the mip level of this texture to attach to the framebuffer.
|
||||
* DEPRECATED: Prefer using multiple #FrameBuffer with different configurations with
|
||||
* `GPU_framebuffer_config_array()`.
|
||||
*/
|
||||
void GPU_framebuffer_texture_cubeface_attach(
|
||||
gpu::FrameBuffer *fb, gpu::Texture *texture, int slot, int face, int mip);
|
||||
|
||||
/**
|
||||
* Detach a texture from a #FrameBuffer. The texture must be attached.
|
||||
* Changes will only take effect next time `GPU_framebuffer_bind()` is called.
|
||||
* DEPRECATED: Prefer using multiple #FrameBuffer with different configurations with
|
||||
* `GPU_framebuffer_config_array()`.
|
||||
*/
|
||||
void GPU_framebuffer_texture_detach(gpu::FrameBuffer *fb, gpu::Texture *texture);
|
||||
|
||||
/**
|
||||
* Checks a framebuffer current configuration for errors.
|
||||
* Checks for texture size mismatch, incompatible attachment, incomplete textures etc...
|
||||
* \note This binds the framebuffer to the active context.
|
||||
* \a err_out is an error output buffer.
|
||||
* \return false if the framebuffer is invalid.
|
||||
*/
|
||||
bool GPU_framebuffer_check_valid(gpu::FrameBuffer *fb, char err_out[256]);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Empty frame-buffer
|
||||
*
|
||||
* An empty frame-buffer is a frame-buffer with no attachments. This allow to rasterize geometry
|
||||
* without creating any dummy attachments and write some computation results using other means
|
||||
* (SSBOs, Images).
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Default size is used if the frame-buffer contains no attachments.
|
||||
* It needs to be re-specified each time an attachment is added.
|
||||
*/
|
||||
void GPU_framebuffer_default_size(gpu::FrameBuffer *fb, int width, int height);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal state
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \brief Get the extent of the framebuffer
|
||||
*/
|
||||
int2 GPU_framebuffer_extent_get(gpu::FrameBuffer *fb);
|
||||
|
||||
/**
|
||||
* Set the viewport offset and size.
|
||||
* These are reset to the original dimensions explicitly (using `GPU_framebuffer_viewport_reset()`)
|
||||
* or when binding the frame-buffer after modifying its attachments.
|
||||
*
|
||||
* \note Viewport and scissor size is stored per frame-buffer.
|
||||
* \note Setting a singular viewport will only change the state of the first viewport.
|
||||
* \note Must be called after first bind.
|
||||
*/
|
||||
void GPU_framebuffer_viewport_set(gpu::FrameBuffer *fb, int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* Similar to `GPU_framebuffer_viewport_set()` but specify the bounds of all 16 viewports.
|
||||
* By default geometry renders only to the first viewport. That can be changed by setting
|
||||
* `gpu_ViewportIndex` in the vertex.
|
||||
*
|
||||
* \note Viewport and scissor size is stored per frame-buffer.
|
||||
* \note Must be called after first bind.
|
||||
*/
|
||||
void GPU_framebuffer_multi_viewports_set(gpu::FrameBuffer *gpu_fb,
|
||||
const int viewport_rects[GPU_MAX_VIEWPORTS][4]);
|
||||
|
||||
/**
|
||||
* Return the viewport offset and size in a int quadruple: (x, y, width, height).
|
||||
* \note Viewport and scissor size is stored per frame-buffer.
|
||||
*/
|
||||
void GPU_framebuffer_viewport_get(gpu::FrameBuffer *fb, int r_viewport[4]);
|
||||
|
||||
/**
|
||||
* Reset a frame-buffer viewport bounds to its attachment(s) size.
|
||||
* \note Viewport and scissor size is stored per frame-buffer.
|
||||
*/
|
||||
void GPU_framebuffer_viewport_reset(gpu::FrameBuffer *fb);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Clearing
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Clear the frame-buffer attachments.
|
||||
* \a buffers controls the types of attachments to clear. Setting GPU_COLOR_BIT will clear *all*
|
||||
* the color attachment.
|
||||
* Each attachment gets cleared to the value of its type:
|
||||
* - color attachments gets cleared to \a clear_col .
|
||||
* - depth attachment gets cleared to \a clear_depth .
|
||||
* - stencil attachment gets cleared to \a clear_stencil .
|
||||
*
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_clear(gpu::FrameBuffer *fb,
|
||||
GPUFrameBufferBits buffers,
|
||||
const double4 clear_col,
|
||||
float clear_depth,
|
||||
unsigned int clear_stencil);
|
||||
|
||||
/**
|
||||
* Clear all color attachment textures with the value \a clear_col .
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_clear_color(gpu::FrameBuffer *fb, const double4 clear_col);
|
||||
|
||||
/**
|
||||
* Clear the depth attachment texture with the value \a clear_depth .
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_clear_depth(gpu::FrameBuffer *fb, float clear_depth);
|
||||
|
||||
/**
|
||||
* Clear the stencil attachment with the value \a clear_stencil .
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_clear_stencil(gpu::FrameBuffer *fb, uint clear_stencil);
|
||||
|
||||
/**
|
||||
* Clear all color attachment textures with the value \a clear_col and the depth attachment texture
|
||||
* with the value \a clear_depth .
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_clear_color_depth(gpu::FrameBuffer *fb,
|
||||
const double4 clear_col,
|
||||
float clear_depth);
|
||||
|
||||
/**
|
||||
* Clear the depth attachment texture with the value \a clear_depth and the stencil attachment with
|
||||
* the value \a clear_stencil .
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_clear_depth_stencil(gpu::FrameBuffer *fb,
|
||||
float clear_depth,
|
||||
uint clear_stencil);
|
||||
/**
|
||||
* Clear the depth attachment texture with the value \a clear_depth , the stencil attachment with
|
||||
* the value \a clear_stencil and all the color attachments with the value \a clear_col .
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_clear_color_depth_stencil(gpu::FrameBuffer *fb,
|
||||
const double4 clear_col,
|
||||
float clear_depth,
|
||||
uint clear_stencil);
|
||||
|
||||
/**
|
||||
* Clear each color attachment texture attached to this frame-buffer with a different color.
|
||||
* IMPORTANT: The size of `clear_colors` must contain at least the number of color attachments.
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
*/
|
||||
void GPU_framebuffer_multi_clear(gpu::FrameBuffer *fb, Span<double4> clear_colors);
|
||||
|
||||
/**
|
||||
* Clear all color attachment textures of the active frame-buffer with the given red, green, blue,
|
||||
* alpha values.
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
* DEPRECATED: Use `GPU_framebuffer_clear_color` with explicit frame-buffer.
|
||||
*/
|
||||
void GPU_clear_color(float red, float green, float blue, float alpha);
|
||||
|
||||
/**
|
||||
* Clear the depth attachment texture of the active frame-buffer with the given depth value.
|
||||
* \note `GPU_write_mask`, and stencil test do not affect this command.
|
||||
* \note Viewport and scissor regions affect this command but are not efficient nor recommended.
|
||||
* DEPRECATED: Use `GPU_framebuffer_clear_color` with explicit frame-buffer.
|
||||
*/
|
||||
void GPU_clear_depth(float depth);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debugging introspection API.
|
||||
* \{ */
|
||||
|
||||
const char *GPU_framebuffer_get_name(gpu::FrameBuffer *fb);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Python API & meta-data
|
||||
*
|
||||
* These are not intrinsic properties of a frame-buffer but they are stored inside the
|
||||
* gpu::FrameBuffer structure for tracking purpose.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
|
||||
* Points to #BPyGPUFrameBuffer.fb
|
||||
*/
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
void **GPU_framebuffer_py_reference_get(gpu::FrameBuffer *fb);
|
||||
void GPU_framebuffer_py_reference_set(gpu::FrameBuffer *fb, void **py_ref);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Keep a stack of bound frame-buffer to allow scoped binding of frame-buffer in python.
|
||||
* This is also used by #GPUOffScreen to save/restore the current frame-buffers.
|
||||
* \note This isn't thread safe.
|
||||
*/
|
||||
/* TODO(fclem): This has nothing to do with the GPU module and should be move to the pyGPU module.
|
||||
*/
|
||||
void GPU_framebuffer_push(gpu::FrameBuffer *fb);
|
||||
gpu::FrameBuffer *GPU_framebuffer_pop();
|
||||
uint GPU_framebuffer_stack_level_get();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Deprecated
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Return true if \a framebuffer is the active framebuffer of the active context.
|
||||
* \note return false if no context is active.
|
||||
* \note this is undefined behavior if \a framebuffer is `nullptr`.
|
||||
* DEPRECATED: Kept only because of Python GPU API. */
|
||||
bool GPU_framebuffer_bound(gpu::FrameBuffer *fb);
|
||||
|
||||
/**
|
||||
* Read a region of the framebuffer depth attachment and copy it to \a r_data .
|
||||
* The pixel data will be converted to \a data_format but it needs to be compatible with the
|
||||
* attachment type. DEPRECATED: Prefer using `GPU_texture_read()`.
|
||||
*/
|
||||
void GPU_framebuffer_read_depth(gpu::FrameBuffer *fb,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
eGPUDataFormat data_format,
|
||||
void *r_data);
|
||||
|
||||
/**
|
||||
* Read a region of a framebuffer color attachment and copy it to \a r_data .
|
||||
* The pixel data will be converted to \a data_format but it needs to be compatible with the
|
||||
* attachment type. DEPRECATED: Prefer using `GPU_texture_read()`.
|
||||
*/
|
||||
void GPU_framebuffer_read_color(gpu::FrameBuffer *fb,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
int slot,
|
||||
eGPUDataFormat data_format,
|
||||
void *r_data);
|
||||
|
||||
/**
|
||||
* Read the color of the window screen as it is currently displayed (so the previously rendered
|
||||
* back-buffer).
|
||||
* DEPRECATED: This isn't even working correctly on some implementation.
|
||||
* TODO: Emulate this by doing some slow texture copy on the backend side or try to read the areas
|
||||
* offscreen textures directly.
|
||||
*/
|
||||
void GPU_frontbuffer_read_color(
|
||||
int x, int y, int width, int height, int channels, eGPUDataFormat data_format, void *r_data);
|
||||
|
||||
/**
|
||||
* Copy the content of \a fb_read attachments to the \a fb_read attachments.
|
||||
* The attachments types are chosen by \a blit_buffers .
|
||||
* Only one color buffer can by copied at a time and its index is chosen by \a read_slot and \a
|
||||
* write_slot.
|
||||
* The source and destination frame-buffers dimensions have to match.
|
||||
* DEPRECATED: Prefer using `GPU_texture_copy()`.
|
||||
*/
|
||||
void GPU_framebuffer_blit(gpu::FrameBuffer *fb_read,
|
||||
int read_slot,
|
||||
gpu::FrameBuffer *fb_write,
|
||||
int write_slot,
|
||||
GPUFrameBufferBits blit_buffers);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU OffScreen
|
||||
*
|
||||
* A `GPUOffScreen` is a convenience type that holds a `GPUFramebuffer` and its associated
|
||||
* `gpu::Texture`s. It is useful for quick drawing surface configuration.
|
||||
* NOTE: They are still limited by the same single context limitation as #FrameBuffer.
|
||||
* \{ */
|
||||
|
||||
struct GPUOffScreen;
|
||||
|
||||
/**
|
||||
* Create a #GPUOffScreen with attachment size of \a width by \a height pixels.
|
||||
* If \a with_depth_buffer is true, a depth buffer attachment will also be created.
|
||||
* \a format is the format of the color buffer.
|
||||
* If \a clear is true, the color and depth buffer attachments will be cleared.
|
||||
* If \a err_out is not `nullptr` it will be use to write any configuration error message..
|
||||
* \note This function binds the framebuffer to the active context.
|
||||
* \note `GPU_TEXTURE_USAGE_ATTACHMENT` is added to the usage parameter by default.
|
||||
*/
|
||||
GPUOffScreen *GPU_offscreen_create(int width,
|
||||
int height,
|
||||
bool with_depth_buffer,
|
||||
gpu::TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
bool clear,
|
||||
char err_out[256]);
|
||||
|
||||
/**
|
||||
* Free a #GPUOffScreen.
|
||||
*/
|
||||
void GPU_offscreen_free(GPUOffScreen *offscreen);
|
||||
|
||||
/**
|
||||
* Unbind a #GPUOffScreen from a #GPUContext.
|
||||
* If \a save is true, it will save the currently bound framebuffer into a stack.
|
||||
*/
|
||||
/* TODO(fclem): Remove the `save` parameter and always save/restore. */
|
||||
void GPU_offscreen_bind(GPUOffScreen *offscreen, bool save);
|
||||
|
||||
/**
|
||||
* Unbind a #GPUOffScreen from a #GPUContext.
|
||||
* If \a restore is true, it will restore the previously bound framebuffer. If false, it will bind
|
||||
* the window back-buffer.
|
||||
*/
|
||||
/* TODO(fclem): Remove the `save` parameter and always save/restore. */
|
||||
void GPU_offscreen_unbind(GPUOffScreen *offscreen, bool restore);
|
||||
|
||||
/**
|
||||
* Read the whole color texture of the #GPUOffScreen.
|
||||
* The pixel data will be converted to \a data_format but it needs to be compatible with the
|
||||
* attachment type.
|
||||
* IMPORTANT: \a r_data must be big enough for all pixels in \a data_format.
|
||||
*/
|
||||
void GPU_offscreen_read_color(GPUOffScreen *offscreen, eGPUDataFormat data_format, void *r_data);
|
||||
/**
|
||||
* A version of #GPU_offscreen_read_color that reads into a region.
|
||||
*/
|
||||
void GPU_offscreen_read_color_region(
|
||||
GPUOffScreen *offscreen, eGPUDataFormat data_format, int x, int y, int w, int h, void *r_data);
|
||||
|
||||
/**
|
||||
* Blit the offscreen color texture to the active framebuffer at the `(x, y)` location.
|
||||
*/
|
||||
void GPU_offscreen_draw_to_screen(GPUOffScreen *offscreen, int x, int y);
|
||||
|
||||
/**
|
||||
* Return the width of a #GPUOffScreen.
|
||||
*/
|
||||
int GPU_offscreen_width(const GPUOffScreen *offscreen);
|
||||
|
||||
/**
|
||||
* Return the height of a #GPUOffScreen.
|
||||
*/
|
||||
int GPU_offscreen_height(const GPUOffScreen *offscreen);
|
||||
|
||||
/**
|
||||
* Return the color texture of a #GPUOffScreen. Does not give ownership.
|
||||
* \note only to be used by viewport code!
|
||||
*/
|
||||
gpu::Texture *GPU_offscreen_color_texture(const GPUOffScreen *offscreen);
|
||||
|
||||
/**
|
||||
* Return the texture format of a #GPUOffScreen.
|
||||
*/
|
||||
gpu::TextureFormat GPU_offscreen_format(const GPUOffScreen *offscreen);
|
||||
|
||||
/**
|
||||
* Return the internals of a #GPUOffScreen. Does not give ownership.
|
||||
* \note only to be used by viewport code!
|
||||
*/
|
||||
void GPU_offscreen_viewport_data_get(GPUOffScreen *offscreen,
|
||||
gpu::FrameBuffer **r_fb,
|
||||
gpu::Texture **r_color,
|
||||
gpu::Texture **r_depth);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
149
blender-5.2.0/source/blender/gpu/GPU_immediate.hh
Normal file
149
blender-5.2.0/source/blender/gpu/GPU_immediate.hh
Normal file
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU immediate mode work-alike
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_immediate_util.hh" /* IWYU pragma: export */
|
||||
#include "GPU_primitive.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_vertex_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class UniformBuf;
|
||||
} // namespace gpu
|
||||
|
||||
/** Returns a cleared vertex format, ready for #add_attr. */
|
||||
GPUVertFormat *immVertexFormat();
|
||||
|
||||
/** Every #immBegin must have a program bound first. */
|
||||
void immBindShader(gpu::Shader *shader);
|
||||
/** Call after your last immEnd, or before binding another program. */
|
||||
void immUnbindProgram();
|
||||
/**
|
||||
* Check if there is a shader bound.
|
||||
*
|
||||
* Useful to trigger asserts when immediate mode drawing and
|
||||
* batch based drawing are mixed. It isn't allowed to have an immediate mode shader bound when a
|
||||
* batch is drawn.
|
||||
*
|
||||
* TODO: We should move these asserts to batch drawing, but didn't do that as it was never forced.
|
||||
*/
|
||||
bool immIsShaderBound();
|
||||
|
||||
/** Must supply exactly vertex_len vertices. */
|
||||
void immBegin(GPUPrimType, uint vertex_len);
|
||||
/** Can supply fewer vertices. */
|
||||
void immBeginAtMost(GPUPrimType, uint max_vertex_len);
|
||||
void immEnd(); /* finishes and draws. */
|
||||
|
||||
/* - #immBegin a batch, then use standard `imm*` functions as usual.
|
||||
* - #immEnd will finalize the batch instead of drawing.
|
||||
*
|
||||
* Then you can draw it as many times as you like!
|
||||
* Partially replaces the need for display lists. */
|
||||
|
||||
gpu::Batch *immBeginBatch(GPUPrimType, uint vertex_len);
|
||||
gpu::Batch *immBeginBatchAtMost(GPUPrimType, uint vertex_len);
|
||||
|
||||
/* Provide attribute values that can change per vertex. */
|
||||
/* First vertex after immBegin must have all its attributes specified. */
|
||||
/* Skipped attributes will continue using the previous value for that attr_id. */
|
||||
void immAttr1f(uint attr_id, float x);
|
||||
void immAttr2f(uint attr_id, float x, float y);
|
||||
void immAttr3f(uint attr_id, float x, float y, float z);
|
||||
void immAttr4f(uint attr_id, float x, float y, float z, float w);
|
||||
|
||||
void immAttr2i(uint attr_id, int x, int y);
|
||||
|
||||
void immAttr1u(uint attr_id, uint x);
|
||||
|
||||
void immAttr2fv(uint attr_id, const float data[2]);
|
||||
void immAttr3fv(uint attr_id, const float data[3]);
|
||||
void immAttr4fv(uint attr_id, const float data[4]);
|
||||
|
||||
void immAttr4ub(uint attr_id, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||
|
||||
void immAttr4ubv(uint attr_id, const unsigned char data[4]);
|
||||
|
||||
/* Explicitly skip an attribute.
|
||||
* This advanced option kills automatic value copying for this attr_id. */
|
||||
|
||||
void immAttrSkip(uint attr_id);
|
||||
|
||||
/* Provide one last attribute value & end the current vertex.
|
||||
* This is most often used for 2D or 3D position (similar to #glVertex). */
|
||||
|
||||
void immVertex2f(uint attr_id, float x, float y);
|
||||
void immVertex3f(uint attr_id, float x, float y, float z);
|
||||
void immVertex4f(uint attr_id, float x, float y, float z, float w);
|
||||
|
||||
void immVertex2i(uint attr_id, int x, int y);
|
||||
|
||||
void immVertex2fv(uint attr_id, const float data[2]);
|
||||
void immVertex3fv(uint attr_id, const float data[3]);
|
||||
|
||||
void immVertex2iv(uint attr_id, const int data[2]);
|
||||
|
||||
/* Provide uniform values that don't change for the entire draw call. */
|
||||
|
||||
void immUniform1i(const char *name, int x);
|
||||
void immUniform1f(const char *name, float x);
|
||||
void immUniform2f(const char *name, float x, float y);
|
||||
void immUniform2fv(const char *name, const float data[2]);
|
||||
void immUniform3f(const char *name, float x, float y, float z);
|
||||
void immUniform3fv(const char *name, const float data[3]);
|
||||
void immUniform4f(const char *name, float x, float y, float z, float w);
|
||||
void immUniform4fv(const char *name, const float data[4]);
|
||||
/**
|
||||
* Note array index is not supported for name (i.e: "array[0]").
|
||||
*/
|
||||
void immUniformArray4fv(const char *bare_name, const float *data, int count);
|
||||
void immUniformMatrix4fv(const char *name, const float data[4][4]);
|
||||
|
||||
void immBindTexture(const char *name, gpu::Texture *tex);
|
||||
void immBindTextureSampler(const char *name, gpu::Texture *tex, GPUSamplerState state);
|
||||
void immBindUniformBuf(const char *name, gpu::UniformBuf *ubo);
|
||||
|
||||
/* Convenience functions for setting "uniform vec4 color". */
|
||||
/* The RGB functions have implicit alpha = 1.0. */
|
||||
|
||||
void immUniformColor4f(float r, float g, float b, float a);
|
||||
void immUniformColor4fv(const float rgba[4]);
|
||||
void immUniformColor3f(float r, float g, float b);
|
||||
void immUniformColor3fv(const float rgb[3]);
|
||||
void immUniformColor3fvAlpha(const float rgb[3], float a);
|
||||
|
||||
void immUniformColor3ub(unsigned char r, unsigned char g, unsigned char b);
|
||||
void immUniformColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||
void immUniformColor3ubv(const unsigned char rgb[3]);
|
||||
void immUniformColor3ubvAlpha(const unsigned char rgb[3], unsigned char a);
|
||||
void immUniformColor4ubv(const unsigned char rgba[4]);
|
||||
|
||||
/**
|
||||
* Extend #immBindShader to use Blender's library of built-in shader programs.
|
||||
* Use #immUnbindProgram() when done.
|
||||
*/
|
||||
void immBindBuiltinProgram(GPUBuiltinShader shader_id);
|
||||
|
||||
/** Extend #immUniformColor to take Blender's themes. */
|
||||
void immUniformThemeColor(int color_id);
|
||||
void immUniformThemeColorAlpha(int color_id, float a);
|
||||
void immUniformThemeColor3(int color_id);
|
||||
void immUniformThemeColorShade(int color_id, int offset);
|
||||
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset);
|
||||
void immUniformThemeColorBlendShade(int color_id1, int color_id2, float fac, int offset);
|
||||
void immUniformThemeColorBlend(int color_id1, int color_id2, float fac);
|
||||
void immThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset);
|
||||
|
||||
} // namespace blender
|
||||
180
blender-5.2.0/source/blender/gpu/GPU_immediate_util.hh
Normal file
180
blender-5.2.0/source/blender/gpu/GPU_immediate_util.hh
Normal file
@@ -0,0 +1,180 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Utility drawing functions (rough equivalent to OpenGL's GLU)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct rctf;
|
||||
|
||||
/* Draw 2D rectangles (replaces glRect functions) */
|
||||
/* caller is responsible for vertex format & shader */
|
||||
void immRectf(uint pos, float x1, float y1, float x2, float y2);
|
||||
void immRecti(uint pos, int x1, int y1, int x2, int y2);
|
||||
void immRectf_with_texco(uint pos, uint tex_coord, const rctf &p, const rctf &uv);
|
||||
|
||||
/**
|
||||
* Same as #immRectf / #immRecti but does not call #immBegin / #immEnd.
|
||||
* To use with #GPU_PRIM_TRIS.
|
||||
*/
|
||||
void immRectf_fast(uint pos, float x1, float y1, float x2, float y2);
|
||||
void immRectf_fast_with_color(
|
||||
uint pos, uint col, float x1, float y1, float x2, float y2, const float color[4]);
|
||||
void immRecti_fast_with_color(
|
||||
uint pos, uint col, int x1, int y1, int x2, int y2, const float color[4]);
|
||||
|
||||
/**
|
||||
* Pack color into 3 bytes
|
||||
*
|
||||
* This define converts a numerical value to the equivalent 24-bit
|
||||
* color, while not being endian-sensitive. On little-endian, this
|
||||
* is the same as doing a 'naive' indexing, on big-endian, it is not!
|
||||
*
|
||||
* \note BGR format (i.e. 0xBBGGRR)...
|
||||
*
|
||||
* \param x: color.
|
||||
*/
|
||||
void imm_cpack(uint x);
|
||||
|
||||
/**
|
||||
* Draw a circle outline with the given \a radius.
|
||||
* The circle is centered at \a x, \a y and drawn in the XY plane.
|
||||
*
|
||||
* \param shdr_pos: The vertex attribute number for position.
|
||||
* \param x: Horizontal center.
|
||||
* \param y: Vertical center.
|
||||
* \param radius: The circle's radius.
|
||||
* \param nsegments: The number of segments to use in drawing (more = smoother).
|
||||
*/
|
||||
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments);
|
||||
/**
|
||||
* Draw a filled circle with the given \a radius.
|
||||
* The circle is centered at \a x, \a y and drawn in the XY plane.
|
||||
*
|
||||
* \param shdr_pos: The vertex attribute number for position.
|
||||
* \param x: Horizontal center.
|
||||
* \param y: Vertical center.
|
||||
* \param radius: The circle's radius.
|
||||
* \param nsegments: The number of segments to use in drawing (more = smoother).
|
||||
*/
|
||||
void imm_draw_circle_fill_2d(uint shdr_pos, float x, float y, float radius, int nsegments);
|
||||
|
||||
void imm_draw_circle_wire_aspect_2d(
|
||||
uint shdr_pos, float x, float y, float radius_x, float radius_y, int nsegments);
|
||||
void imm_draw_circle_fill_aspect_2d(
|
||||
uint shdr_pos, float x, float y, float radius_x, float radius_y, int nsegments);
|
||||
|
||||
/**
|
||||
* Use this version when #GPUVertFormat has a vec3 position.
|
||||
*/
|
||||
void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments);
|
||||
void imm_draw_circle_wire_aspect_3d(
|
||||
uint pos, float x, float y, float radius_x, float radius_y, int nsegments);
|
||||
void imm_draw_circle_dashed_3d(uint pos, float x, float y, float radius, int nsegments);
|
||||
void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegments);
|
||||
void imm_draw_circle_fill_aspect_3d(
|
||||
uint pos, float x, float y, float radius_x, float radius_y, int nsegments);
|
||||
|
||||
/**
|
||||
* Same as 'imm_draw_disk_partial_fill_2d', except it draws a wire arc.
|
||||
*/
|
||||
void imm_draw_circle_partial_wire_2d(
|
||||
uint pos, float x, float y, float radius, int nsegments, float start, float sweep);
|
||||
void imm_draw_circle_partial_wire_3d(
|
||||
uint pos, float x, float y, float z, float radius, int nsegments, float start, float sweep);
|
||||
|
||||
/**
|
||||
* Draw a filled arc with the given inner and outer radius.
|
||||
* The circle is centered at \a x, \a y and drawn in the XY plane.
|
||||
*
|
||||
* \note Arguments are `gluPartialDisk` compatible.
|
||||
*
|
||||
* \param pos: The vertex attribute number for position.
|
||||
* \param x: Horizontal center.
|
||||
* \param y: Vertical center.
|
||||
* \param rad_inner: The inner circle's radius.
|
||||
* \param rad_outer: The outer circle's radius (can be zero).
|
||||
* \param nsegments: The number of segments to use in drawing (more = smoother).
|
||||
* \param start: Specifies the starting angle, in degrees, of the disk portion.
|
||||
* \param sweep: Specifies the sweep angle, in degrees, of the disk portion.
|
||||
*/
|
||||
void imm_draw_disk_partial_fill_2d(uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float rad_inner,
|
||||
float rad_outer,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep);
|
||||
void imm_draw_disk_partial_fill_3d(uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float rad_inner,
|
||||
float rad_outer,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep);
|
||||
|
||||
/**
|
||||
* Draw a lined box.
|
||||
*
|
||||
* \param pos: The vertex attribute number for position.
|
||||
* \param x1: left.
|
||||
* \param y1: bottom.
|
||||
* \param x2: right.
|
||||
* \param y2: top.
|
||||
*/
|
||||
void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2);
|
||||
void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2);
|
||||
|
||||
/**
|
||||
* Draw a standard checkerboard to indicate transparent backgrounds.
|
||||
*/
|
||||
void imm_draw_box_checker_2d_ex(float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
const float color_primary[4],
|
||||
const float color_secondary[4],
|
||||
int checker_size);
|
||||
void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2, bool clear_alpha = false);
|
||||
|
||||
void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]);
|
||||
void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]);
|
||||
void imm_draw_cube_corners_3d(uint pos,
|
||||
const float center[3],
|
||||
const float aspect[3],
|
||||
float factor);
|
||||
|
||||
/**
|
||||
* Draw a cylinder. Replacement for #gluCylinder.
|
||||
* \warning Slow, better use it only if you no other choices.
|
||||
*
|
||||
* \param pos: The vertex attribute number for position.
|
||||
* \param nor: The vertex attribute number for normal.
|
||||
* \param base: Specifies the radius of the cylinder at z = 0.
|
||||
* \param top: Specifies the radius of the cylinder at z = height.
|
||||
* \param height: Specifies the height of the cylinder.
|
||||
* \param slices: Specifies the number of subdivisions around the z axis.
|
||||
* \param stacks: Specifies the number of subdivisions along the z axis.
|
||||
*/
|
||||
void imm_draw_cylinder_fill_normal_3d(
|
||||
uint pos, uint nor, float base, float top, float height, int slices, int stacks);
|
||||
void imm_draw_cylinder_wire_3d(
|
||||
uint pos, float base, float top, float height, int slices, int stacks);
|
||||
void imm_draw_cylinder_fill_3d(
|
||||
uint pos, float base, float top, float height, int slices, int stacks);
|
||||
|
||||
void imm_drawcircball(const float cent[3], float radius, const float tmat[4][4], uint pos);
|
||||
|
||||
} // namespace blender
|
||||
291
blender-5.2.0/source/blender/gpu/GPU_index_buffer.hh
Normal file
291
blender-5.2.0/source/blender/gpu/GPU_index_buffer.hh
Normal file
@@ -0,0 +1,291 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU index buffer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "GPU_primitive.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define GPU_TRACK_INDEX_RANGE 1
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/** Value for invisible elements in a #GPU_PRIM_POINTS index buffer. */
|
||||
constexpr uint32_t RESTART_INDEX = 0xFFFFFFFF;
|
||||
|
||||
enum GPUIndexBufType {
|
||||
GPU_INDEX_U16,
|
||||
GPU_INDEX_U32,
|
||||
};
|
||||
|
||||
inline size_t to_bytesize(GPUIndexBufType type)
|
||||
{
|
||||
return (type == GPU_INDEX_U32) ? sizeof(uint32_t) : sizeof(uint16_t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class which is then specialized for each implementation (GL, VK, ...).
|
||||
*
|
||||
* \note #IndexBuf does not hold any #GPUPrimType.
|
||||
* This is because it can be interpreted differently by multiple batches.
|
||||
*/
|
||||
class IndexBuf {
|
||||
protected:
|
||||
/** Type of indices used inside this buffer. */
|
||||
GPUIndexBufType index_type_ = GPU_INDEX_U32;
|
||||
/** Offset in this buffer to the first index to render. Is 0 if not a subrange. */
|
||||
uint32_t index_start_ = 0;
|
||||
/** Number of indices to render. */
|
||||
uint32_t index_len_ = 0;
|
||||
/** Base index: Added to all indices after fetching. Allows index compression. */
|
||||
uint32_t index_base_ = 0;
|
||||
/** Bookkeeping. */
|
||||
bool is_init_ = false;
|
||||
/** Is this object only a reference to a subrange of another IndexBuf. */
|
||||
bool is_subrange_ = false;
|
||||
/** True if buffer only contains restart indices. */
|
||||
bool is_empty_ = false;
|
||||
|
||||
union {
|
||||
/** Mapped buffer data. non-NULL indicates not yet sent to VRAM. */
|
||||
void *data_ = nullptr;
|
||||
/** If is_subrange is true, this is the source index buffer. */
|
||||
IndexBuf *src_;
|
||||
};
|
||||
|
||||
public:
|
||||
IndexBuf() {};
|
||||
virtual ~IndexBuf();
|
||||
|
||||
void init(uint indices_len,
|
||||
uint32_t *indices,
|
||||
uint min_index,
|
||||
uint max_index,
|
||||
GPUPrimType prim_type,
|
||||
bool uses_restart_indices);
|
||||
void init_subrange(IndexBuf *elem_src, uint start, uint length);
|
||||
void init_build_on_device(uint index_len);
|
||||
|
||||
/* Returns render index count (not precise). */
|
||||
uint32_t index_len_get() const
|
||||
{
|
||||
/* Return 0 to bypass drawing for index buffers full of restart indices.
|
||||
* They can lead to graphical glitches on some systems. (See #96892) */
|
||||
return is_empty_ ? 0 : index_len_;
|
||||
}
|
||||
uint32_t index_start_get() const
|
||||
{
|
||||
return index_start_;
|
||||
}
|
||||
uint32_t index_base_get() const
|
||||
{
|
||||
return index_base_;
|
||||
}
|
||||
bool is_32bit() const
|
||||
{
|
||||
return index_type_ == GPU_INDEX_U32;
|
||||
}
|
||||
/* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */
|
||||
size_t size_get() const
|
||||
{
|
||||
return index_len_ * to_bytesize(index_type_);
|
||||
};
|
||||
|
||||
bool is_init() const
|
||||
{
|
||||
return is_init_;
|
||||
};
|
||||
|
||||
virtual void upload_data() = 0;
|
||||
|
||||
virtual void bind_as_ssbo(uint binding) = 0;
|
||||
|
||||
virtual void read(uint32_t *data) const = 0;
|
||||
|
||||
virtual void update_sub(uint start, uint len, const void *data) = 0;
|
||||
|
||||
private:
|
||||
inline void squeeze_indices_short(uint min_idx,
|
||||
uint max_idx,
|
||||
GPUPrimType prim_type,
|
||||
bool clamp_indices_in_range);
|
||||
virtual void strip_restart_indices() = 0;
|
||||
};
|
||||
|
||||
inline int indices_per_primitive(GPUPrimType prim_type)
|
||||
{
|
||||
switch (prim_type) {
|
||||
case GPU_PRIM_POINTS:
|
||||
return 1;
|
||||
case GPU_PRIM_LINES:
|
||||
return 2;
|
||||
case GPU_PRIM_TRIS:
|
||||
return 3;
|
||||
case GPU_PRIM_LINES_ADJ:
|
||||
return 4;
|
||||
case GPU_PRIM_TRIS_ADJ:
|
||||
return 6;
|
||||
/** IMPORTANT: These last two expects no restart primitive.
|
||||
* Asserting for this would be too slow. Just don't be stupid.
|
||||
* This is needed for polylines but should be deprecated.
|
||||
* See GPU_batch_draw_expanded_parameter_get */
|
||||
case GPU_PRIM_LINE_STRIP:
|
||||
return 1; /* Minus one for the whole length. */
|
||||
case GPU_PRIM_LINE_LOOP:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
gpu::IndexBuf *GPU_indexbuf_calloc();
|
||||
|
||||
struct GPUIndexBufBuilder {
|
||||
uint max_allowed_index;
|
||||
uint max_index_len;
|
||||
uint index_len;
|
||||
uint index_min;
|
||||
uint index_max;
|
||||
uint restart_index_value;
|
||||
bool uses_restart_indices;
|
||||
|
||||
GPUPrimType prim_type;
|
||||
uint32_t *data;
|
||||
};
|
||||
|
||||
/** Supports all primitive types. */
|
||||
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uint vertex_len);
|
||||
|
||||
/** Supports only #GPU_PRIM_POINTS, #GPU_PRIM_LINES and #GPU_PRIM_TRIS. */
|
||||
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len);
|
||||
gpu::IndexBuf *GPU_indexbuf_build_on_device(uint index_len);
|
||||
|
||||
void GPU_indexbuf_init_build_on_device(gpu::IndexBuf *elem, uint index_len);
|
||||
|
||||
MutableSpan<uint32_t> GPU_indexbuf_get_data(GPUIndexBufBuilder *);
|
||||
|
||||
/*
|
||||
* Thread safe.
|
||||
*
|
||||
* Function inspired by the reduction directives of multi-thread work API's.
|
||||
*/
|
||||
void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *builder_from);
|
||||
|
||||
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v);
|
||||
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *);
|
||||
|
||||
void GPU_indexbuf_add_point_vert(GPUIndexBufBuilder *, uint v);
|
||||
void GPU_indexbuf_add_line_verts(GPUIndexBufBuilder *, uint v1, uint v2);
|
||||
void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3);
|
||||
void GPU_indexbuf_add_line_adj_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3, uint v4);
|
||||
|
||||
void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1);
|
||||
void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2);
|
||||
void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3);
|
||||
|
||||
/* Skip primitive rendering at the given index. */
|
||||
void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem);
|
||||
void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem);
|
||||
void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem);
|
||||
|
||||
gpu::IndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *);
|
||||
gpu::IndexBuf *GPU_indexbuf_build_ex(GPUIndexBufBuilder *builder,
|
||||
uint index_min,
|
||||
uint index_max,
|
||||
bool uses_restart_indices);
|
||||
void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, gpu::IndexBuf *);
|
||||
void GPU_indexbuf_build_in_place_ex(GPUIndexBufBuilder *builder,
|
||||
uint index_min,
|
||||
uint index_max,
|
||||
bool uses_restart_indices,
|
||||
gpu::IndexBuf *elem);
|
||||
|
||||
/**
|
||||
* Fill an IBO by uploading the referenced data directly to the GPU, bypassing the separate storage
|
||||
* in the IBO. This should be used whenever the equivalent indices already exist in a contiguous
|
||||
* array on the CPU.
|
||||
*
|
||||
* \todo The optimization to avoid the local copy currently isn't implemented.
|
||||
*/
|
||||
gpu::IndexBuf *GPU_indexbuf_build_from_memory(GPUPrimType prim_type,
|
||||
const uint32_t *data,
|
||||
int32_t data_len,
|
||||
int32_t index_min,
|
||||
int32_t index_max,
|
||||
bool uses_restart_indices);
|
||||
|
||||
/**
|
||||
* \note Sub-ranges are not taken into account, the whole buffer will be bound without any offset.
|
||||
*/
|
||||
void GPU_indexbuf_bind_as_ssbo(gpu::IndexBuf *elem, int binding);
|
||||
|
||||
gpu::IndexBuf *GPU_indexbuf_build_curves_on_device(GPUPrimType prim_type,
|
||||
uint curves_num,
|
||||
uint verts_per_curve);
|
||||
|
||||
/* Upload data to the GPU (if not built on the device) and bind the buffer to its default target.
|
||||
*/
|
||||
void GPU_indexbuf_use(gpu::IndexBuf *elem);
|
||||
|
||||
/* Partially update the gpu::IndexBuf which was already sent to the device, or built
|
||||
* directly on the device. The data needs to be compatible with potential compression applied to
|
||||
* the original indices when the index buffer was built, i.e., if the data was compressed to use
|
||||
* shorts instead of ints, shorts should passed here. */
|
||||
void GPU_indexbuf_update_sub(gpu::IndexBuf *elem, uint start, uint len, const void *data);
|
||||
|
||||
/* Create a sub-range of an existing index-buffer. */
|
||||
gpu::IndexBuf *GPU_indexbuf_create_subrange(gpu::IndexBuf *elem_src, uint start, uint length);
|
||||
void GPU_indexbuf_create_subrange_in_place(gpu::IndexBuf *elem,
|
||||
gpu::IndexBuf *elem_src,
|
||||
uint start,
|
||||
uint length);
|
||||
|
||||
/**
|
||||
* (Download and) fill data with the contents of the index buffer.
|
||||
*
|
||||
* NOTE: caller is responsible to reserve enough memory.
|
||||
*/
|
||||
void GPU_indexbuf_read(gpu::IndexBuf *elem, uint32_t *data);
|
||||
|
||||
void GPU_indexbuf_discard(gpu::IndexBuf *elem);
|
||||
|
||||
bool GPU_indexbuf_is_init(gpu::IndexBuf *elem);
|
||||
|
||||
int GPU_indexbuf_primitive_len(GPUPrimType prim_type);
|
||||
|
||||
/* Macros */
|
||||
|
||||
#define GPU_INDEXBUF_DISCARD_SAFE(elem) \
|
||||
do { \
|
||||
if (elem != nullptr) { \
|
||||
GPU_indexbuf_discard(elem); \
|
||||
elem = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class IndexBufDeleter {
|
||||
public:
|
||||
void operator()(IndexBuf *ibo)
|
||||
{
|
||||
GPU_indexbuf_discard(ibo);
|
||||
}
|
||||
};
|
||||
|
||||
using IndexBufPtr = std::unique_ptr<IndexBuf, IndexBufDeleter>;
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/gpu/GPU_init_exit.hh
Normal file
17
blender-5.2.0/source/blender/gpu/GPU_init_exit.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
void GPU_init();
|
||||
void GPU_exit();
|
||||
bool GPU_is_init();
|
||||
|
||||
} // namespace blender
|
||||
490
blender-5.2.0/source/blender/gpu/GPU_material.hh
Normal file
490
blender-5.2.0/source/blender/gpu/GPU_material.hh
Normal file
@@ -0,0 +1,490 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "DNA_customdata_types.h" /* for eCustomDataType */
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "GPU_shader.hh" /* for GPUShaderCreateInfo */
|
||||
#include "GPU_texture.hh" /* for GPUSamplerState */
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GHash;
|
||||
struct GPUMaterial;
|
||||
struct GPUInput;
|
||||
struct GPUNodeLink;
|
||||
struct GPUNodeStack;
|
||||
struct GPUPass;
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
class UniformBuf;
|
||||
} // namespace gpu
|
||||
struct Image;
|
||||
struct ImageUser;
|
||||
struct Main;
|
||||
struct Material;
|
||||
struct Scene;
|
||||
struct bNode;
|
||||
struct bNodeTree;
|
||||
|
||||
/**
|
||||
* High level functions to create and use GPU materials.
|
||||
*/
|
||||
|
||||
enum eGPUMaterialEngine {
|
||||
GPU_MAT_EEVEE,
|
||||
GPU_MAT_COMPOSITOR,
|
||||
GPU_MAT_ENGINE_MAX,
|
||||
};
|
||||
|
||||
enum GPUMaterialStatus {
|
||||
GPU_MAT_FAILED = 0,
|
||||
GPU_MAT_QUEUED,
|
||||
GPU_MAT_SUCCESS,
|
||||
};
|
||||
|
||||
/* GPU_MAT_OPTIMIZATION_SKIP for cases where we do not
|
||||
* plan to perform optimization on a given material. */
|
||||
enum eGPUMaterialOptimizationStatus {
|
||||
GPU_MAT_OPTIMIZATION_SKIP = 0,
|
||||
GPU_MAT_OPTIMIZATION_QUEUED,
|
||||
GPU_MAT_OPTIMIZATION_SUCCESS,
|
||||
};
|
||||
|
||||
enum eGPUMaterialFlag {
|
||||
GPU_MATFLAG_DIFFUSE = (1 << 0),
|
||||
GPU_MATFLAG_SUBSURFACE = (1 << 1),
|
||||
GPU_MATFLAG_GLOSSY = (1 << 2),
|
||||
GPU_MATFLAG_REFRACT = (1 << 3),
|
||||
GPU_MATFLAG_EMISSION = (1 << 4),
|
||||
GPU_MATFLAG_TRANSPARENT = (1 << 5),
|
||||
GPU_MATFLAG_HOLDOUT = (1 << 6),
|
||||
GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7),
|
||||
GPU_MATFLAG_AO = (1 << 8),
|
||||
/* Signals the presence of multiple reflection closures. */
|
||||
GPU_MATFLAG_COAT = (1 << 9),
|
||||
GPU_MATFLAG_TRANSLUCENT = (1 << 10),
|
||||
GPU_MATFLAG_RAYCAST = (1 << 11),
|
||||
|
||||
GPU_MATFLAG_VOLUME_SCATTER = (1 << 16),
|
||||
GPU_MATFLAG_VOLUME_ABSORPTION = (1 << 17),
|
||||
|
||||
GPU_MATFLAG_OBJECT_INFO = (1 << 18),
|
||||
GPU_MATFLAG_AOV = (1 << 19),
|
||||
|
||||
GPU_MATFLAG_BARYCENTRIC = (1 << 20),
|
||||
/* Signals that these specific closures might *not* be colorless.
|
||||
* If this flag is not set, all closures are ensured to not be tinted. */
|
||||
GPU_MATFLAG_REFLECTION_MAYBE_COLORED = (1 << 21),
|
||||
GPU_MATFLAG_REFRACTION_MAYBE_COLORED = (1 << 22),
|
||||
GPU_MATFLAG_TRANSPARENT_MAYBE_COLORED = (1 << 23),
|
||||
|
||||
/* Set if the material uses the "Is Diffuse / Glossy Ray" output of the light path node. */
|
||||
GPU_MATFLAG_IS_DIFFUSE_OR_GLOSSY_RAY_FLAG = (1 << 24),
|
||||
|
||||
/* Signals scene time use. */
|
||||
GPU_MATFLAG_SCENE_TIME = (1 << 25),
|
||||
|
||||
/* Tells the render engine the material was just compiled or updated. */
|
||||
GPU_MATFLAG_UPDATED = (1 << 29),
|
||||
};
|
||||
ENUM_OPERATORS(eGPUMaterialFlag);
|
||||
|
||||
using GPUCodegenCallbackFn = void (*)(void *thunk,
|
||||
GPUMaterial *mat,
|
||||
struct GPUCodegenOutput *codegen);
|
||||
/**
|
||||
* Should return an already compiled pass if it's functionally equivalent to the one being
|
||||
* compiled.
|
||||
*/
|
||||
using GPUMaterialPassReplacementCallbackFn = GPUPass *(*)(void *thunk, GPUMaterial *mat);
|
||||
|
||||
struct GPUMaterialFromNodeTreeResult {
|
||||
GPUMaterial *material = nullptr;
|
||||
|
||||
struct Error {
|
||||
const bNode *node;
|
||||
std::string message;
|
||||
};
|
||||
Vector<Error> errors;
|
||||
};
|
||||
|
||||
/** WARNING: gpumaterials thread safety must be ensured by the caller. */
|
||||
GPUMaterialFromNodeTreeResult GPU_material_from_nodetree(
|
||||
Material *ma,
|
||||
bNodeTree *ntree,
|
||||
ListBaseT<LinkData> *gpumaterials,
|
||||
const char *name,
|
||||
eGPUMaterialEngine engine,
|
||||
uint64_t shader_uuid,
|
||||
bool deferred_compilation,
|
||||
GPUCodegenCallbackFn callback,
|
||||
void *thunk,
|
||||
GPUMaterialPassReplacementCallbackFn pass_replacement_cb = nullptr);
|
||||
|
||||
/* A callback passed to GPU_material_from_callbacks to construct the material graph by adding and
|
||||
* linking the necessary GPU material nodes. */
|
||||
using ConstructGPUMaterialFn = void (*)(void *thunk, GPUMaterial *material);
|
||||
|
||||
/* Construct a GPU material from a set of callbacks. See the callback types for more information.
|
||||
* The given thunk will be passed as the first parameter of each callback. */
|
||||
GPUMaterial *GPU_material_from_callbacks(eGPUMaterialEngine engine,
|
||||
ConstructGPUMaterialFn construct_function_cb,
|
||||
GPUCodegenCallbackFn generate_code_function_cb,
|
||||
void *thunk);
|
||||
|
||||
void GPU_material_free_single(GPUMaterial *material);
|
||||
void GPU_material_free(ListBaseT<LinkData> *gpumaterial);
|
||||
|
||||
void GPU_materials_free(Main *bmain);
|
||||
|
||||
GPUPass *GPU_material_get_pass(GPUMaterial *material);
|
||||
/** Return the most optimal shader configuration for the given material. */
|
||||
gpu::Shader *GPU_material_get_shader(GPUMaterial *material);
|
||||
|
||||
const char *GPU_material_get_name(GPUMaterial *material);
|
||||
|
||||
/**
|
||||
* Return can be null if it's a world material.
|
||||
*/
|
||||
Material *GPU_material_get_material(GPUMaterial *material);
|
||||
/**
|
||||
* Return true if the material compilation has not yet begin or begin.
|
||||
*/
|
||||
GPUMaterialStatus GPU_material_status(GPUMaterial *mat);
|
||||
|
||||
/**
|
||||
* Return status for asynchronous optimization jobs.
|
||||
*/
|
||||
eGPUMaterialOptimizationStatus GPU_material_optimization_status(GPUMaterial *mat);
|
||||
|
||||
uint64_t GPU_material_compilation_timestamp(GPUMaterial *mat);
|
||||
|
||||
gpu::UniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material);
|
||||
/**
|
||||
* Create dynamic UBO from parameters
|
||||
*
|
||||
* \param inputs: Items are #LinkData, data is #GPUInput (`BLI_genericNodeN(GPUInput)`).
|
||||
*/
|
||||
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBaseT<LinkData> *inputs);
|
||||
|
||||
bool GPU_material_has_surface_output(GPUMaterial *mat);
|
||||
bool GPU_material_has_volume_output(GPUMaterial *mat);
|
||||
bool GPU_material_has_displacement_output(GPUMaterial *mat);
|
||||
|
||||
bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag);
|
||||
|
||||
uint64_t GPU_material_uuid_get(GPUMaterial *mat);
|
||||
|
||||
struct GPULayerAttr {
|
||||
GPULayerAttr *next, *prev;
|
||||
|
||||
/* Meaningful part of the attribute set key. */
|
||||
char name[256]; /* Multiple MAX_CUSTOMDATA_LAYER_NAME */
|
||||
/** Hash of `name[68]`. */
|
||||
uint32_t hash_code;
|
||||
|
||||
/* Helper fields used by code generation. */
|
||||
int users;
|
||||
};
|
||||
|
||||
const ListBaseT<GPULayerAttr> *GPU_material_layer_attributes(const GPUMaterial *material);
|
||||
|
||||
/* Requested Material Attributes and Textures */
|
||||
|
||||
enum GPUType {
|
||||
/* Keep in sync with GPU_DATATYPE_STR */
|
||||
/* The value indicates the number of elements in each type */
|
||||
GPU_NONE = 0,
|
||||
GPU_FLOAT = 1,
|
||||
GPU_VEC2 = 2,
|
||||
GPU_VEC3 = 3,
|
||||
GPU_VEC4 = 4,
|
||||
GPU_MAT3 = 9,
|
||||
GPU_MAT4 = 16,
|
||||
GPU_MAX_CONSTANT_DATA = GPU_MAT4,
|
||||
|
||||
/* Values not in GPU_DATATYPE_STR */
|
||||
GPU_TEX1D_ARRAY = 1001,
|
||||
GPU_TEX2D = 1002,
|
||||
GPU_TEX2D_ARRAY = 1003,
|
||||
GPU_TEX3D = 1004,
|
||||
|
||||
/* GLSL Struct types */
|
||||
GPU_CLOSURE = 1007,
|
||||
|
||||
/* Opengl Attributes */
|
||||
GPU_ATTR = 3001,
|
||||
};
|
||||
|
||||
enum GPUDefaultValue {
|
||||
GPU_DEFAULT_0 = 0,
|
||||
GPU_DEFAULT_1,
|
||||
};
|
||||
|
||||
struct GPUMaterialAttribute {
|
||||
GPUMaterialAttribute *next, *prev;
|
||||
int type; /* eCustomDataType */
|
||||
char name[/*MAX_CUSTOMDATA_LAYER_NAME*/ 68];
|
||||
char input_name[/*GPU_MAX_SAFE_ATTR_NAME + 1*/ 12 + 1];
|
||||
GPUType gputype;
|
||||
GPUDefaultValue default_value; /* Only for volumes attributes. */
|
||||
int id;
|
||||
int users;
|
||||
/**
|
||||
* If true, the corresponding attribute is the specified default color attribute on the mesh,
|
||||
* if it exists. In that case the type and name data can vary per geometry, so it will not be
|
||||
* valid here.
|
||||
*/
|
||||
bool is_default_color;
|
||||
/**
|
||||
* If true, the attribute is the length of hair particles and curves.
|
||||
*/
|
||||
bool is_hair_length;
|
||||
/**
|
||||
* If true, the attribute is the intercept of hair particles and curves.
|
||||
*/
|
||||
bool is_hair_intercept;
|
||||
};
|
||||
|
||||
struct GPUMaterialTexture {
|
||||
GPUMaterialTexture *next, *prev;
|
||||
Image *ima;
|
||||
ImageUser iuser;
|
||||
bool iuser_available;
|
||||
gpu::Texture **colorband;
|
||||
gpu::Texture **sky;
|
||||
char sampler_name[32]; /* Name of sampler in GLSL. */
|
||||
char tiled_mapping_name[32]; /* Name of tile mapping sampler in GLSL. */
|
||||
int users;
|
||||
GPUSamplerState sampler_state;
|
||||
};
|
||||
|
||||
ListBaseT<GPUMaterialAttribute> GPU_material_attributes(const GPUMaterial *material);
|
||||
ListBaseT<GPUMaterialTexture> GPU_material_textures(GPUMaterial *material);
|
||||
|
||||
struct GPUUniformAttr {
|
||||
GPUUniformAttr *next, *prev;
|
||||
|
||||
/* Meaningful part of the attribute set key. */
|
||||
char name[/*MAX_CUSTOMDATA_LAYER_NAME*/ 68];
|
||||
/** Hash of `name[MAX_CUSTOMDATA_LAYER_NAME] + use_dupli`. */
|
||||
uint32_t hash_code;
|
||||
bool use_dupli;
|
||||
|
||||
/* Helper fields used by code generation. */
|
||||
short id;
|
||||
int users;
|
||||
};
|
||||
|
||||
struct GPUUniformAttrList {
|
||||
ListBaseT<GPUUniformAttr> list;
|
||||
|
||||
/* List length and hash code precomputed for fast lookup and comparison. */
|
||||
unsigned int count, hash_code;
|
||||
};
|
||||
|
||||
const GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material);
|
||||
|
||||
/* Functions to create GPU Materials nodes. */
|
||||
/* TODO: Move to its own header. */
|
||||
|
||||
struct GPUNodeStack {
|
||||
GPUType type;
|
||||
float vec[4];
|
||||
GPUNodeLink *link;
|
||||
bool hasinput;
|
||||
bool hasoutput;
|
||||
short sockettype;
|
||||
bool end;
|
||||
|
||||
/* Return true if the socket might contain a polychromatic value.
|
||||
* This is a conservative heuristic that allows for optimization. */
|
||||
bool might_be_tinted() const
|
||||
{
|
||||
return this->link || (this->vec[0] != this->vec[1]) || (this->vec[1] != this->vec[2]);
|
||||
}
|
||||
|
||||
bool socket_not_zero() const
|
||||
{
|
||||
return this->link || (saturate_f(this->vec[0]) > near_zero);
|
||||
}
|
||||
|
||||
bool socket_not_one() const
|
||||
{
|
||||
return this->link || (saturate_f(this->vec[0]) < near_one);
|
||||
}
|
||||
|
||||
bool socket_not_black() const
|
||||
{
|
||||
return this->link || saturate_f(this->vec[0]) > near_zero ||
|
||||
saturate_f(this->vec[1]) > near_zero || saturate_f(this->vec[2]) > near_zero;
|
||||
}
|
||||
|
||||
bool socket_not_white() const
|
||||
{
|
||||
return this->link || saturate_f(this->vec[0]) < near_one ||
|
||||
saturate_f(this->vec[1]) < near_one || saturate_f(this->vec[2]) < near_one;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float near_zero = 1e-5f;
|
||||
static constexpr float near_one = 1.0f - 1e-5f;
|
||||
float saturate_f(const float f) const
|
||||
{
|
||||
return clamp_f(f, 0.0f, 1.0f);
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUGraphOutput {
|
||||
std::string serialized;
|
||||
Vector<StringRefNull> dependencies;
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return serialized.empty();
|
||||
}
|
||||
|
||||
std::string serialized_or_default(std::string value) const
|
||||
{
|
||||
return serialized.empty() ? value : serialized;
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUCodegenOutput {
|
||||
std::string attr_load;
|
||||
/* Node-tree functions calls. */
|
||||
GPUGraphOutput displacement;
|
||||
GPUGraphOutput surface;
|
||||
GPUGraphOutput volume;
|
||||
GPUGraphOutput thickness;
|
||||
GPUGraphOutput composite;
|
||||
Vector<GPUGraphOutput> material_functions;
|
||||
|
||||
GPUShaderCreateInfo *create_info;
|
||||
};
|
||||
|
||||
GPUNodeLink *GPU_constant(const float *num);
|
||||
GPUNodeLink *GPU_uniform(const float *num);
|
||||
GPUNodeLink *GPU_attribute(GPUMaterial *mat, eCustomDataType type, const char *name);
|
||||
/**
|
||||
* Add a GPU attribute that refers to the default color attribute on a geometry.
|
||||
* The name, type, and domain are unknown and do not depend on the material.
|
||||
*/
|
||||
GPUNodeLink *GPU_attribute_default_color(GPUMaterial *mat);
|
||||
/**
|
||||
* Add a GPU attribute that refers to the approximate length of curves/hairs.
|
||||
*/
|
||||
GPUNodeLink *GPU_attribute_hair_length(GPUMaterial *mat);
|
||||
GPUNodeLink *GPU_attribute_hair_intercept(GPUMaterial *mat);
|
||||
GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat,
|
||||
eCustomDataType type,
|
||||
const char *name,
|
||||
GPUDefaultValue default_value);
|
||||
GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat,
|
||||
const char *name,
|
||||
bool use_dupli,
|
||||
uint32_t *r_hash);
|
||||
GPUNodeLink *GPU_layer_attribute(GPUMaterial *mat, const char *name);
|
||||
GPUNodeLink *GPU_image(GPUMaterial *mat,
|
||||
Image *ima,
|
||||
ImageUser *iuser,
|
||||
GPUSamplerState sampler_state);
|
||||
void GPU_image_tiled(GPUMaterial *mat,
|
||||
Image *ima,
|
||||
ImageUser *iuser,
|
||||
GPUSamplerState sampler_state,
|
||||
GPUNodeLink **r_image_tiled_link,
|
||||
GPUNodeLink **r_image_tiled_mapping_link);
|
||||
GPUNodeLink *GPU_image_sky(GPUMaterial *mat,
|
||||
int width,
|
||||
int height,
|
||||
const float *pixels,
|
||||
float *layer,
|
||||
GPUSamplerState sampler_state);
|
||||
GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *r_row);
|
||||
|
||||
/**
|
||||
* Create an implementation defined differential calculation of a float function.
|
||||
* The given function should return a float.
|
||||
* The result will be a vec2 containing dFdx and dFdy result of that function.
|
||||
*/
|
||||
GPUNodeLink *GPU_differentiate_float_function(const char *function_name, const float filter_width);
|
||||
|
||||
bool GPU_link(GPUMaterial *mat, const char *name, ...);
|
||||
bool GPU_stack_link(GPUMaterial *mat,
|
||||
const bNode *node,
|
||||
const char *name,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out,
|
||||
...);
|
||||
|
||||
bool GPU_stack_link_zone(GPUMaterial *material,
|
||||
const bNode *bnode,
|
||||
const char *name,
|
||||
GPUNodeStack *in,
|
||||
GPUNodeStack *out,
|
||||
int zone_index,
|
||||
bool is_zone_end,
|
||||
int in_argument_count,
|
||||
int out_argument_count);
|
||||
|
||||
void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link);
|
||||
void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link);
|
||||
void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link);
|
||||
void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link);
|
||||
|
||||
void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash);
|
||||
|
||||
void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link);
|
||||
|
||||
/**
|
||||
* Wrap a part of the material graph into a function. You need then need to call the function by
|
||||
* using something like #GPU_differentiate_float_function.
|
||||
* \note This replace the link by a constant to break the link with the main graph.
|
||||
* \param return_type: sub function return type. Output is cast to this type.
|
||||
* \param link: link to use as the sub function output.
|
||||
* \return the name of the generated function.
|
||||
*/
|
||||
char *GPU_material_split_sub_function(GPUMaterial *material,
|
||||
GPUType return_type,
|
||||
GPUNodeLink **link);
|
||||
|
||||
void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag);
|
||||
eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat);
|
||||
|
||||
GHash *GPU_uniform_attr_list_hash_new(const char *info);
|
||||
void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, const GPUUniformAttrList *src);
|
||||
void GPU_uniform_attr_list_free(GPUUniformAttrList *set);
|
||||
|
||||
/* Returns the GPU node stack of the input with the given identifier in the given node within the
|
||||
* given inputs stack array. */
|
||||
GPUNodeStack &GPU_node_get_input(const bNode &node, GPUNodeStack inputs[], StringRef identifier);
|
||||
|
||||
/* Returns the GPU node stack of the output with the given identifier in the given node within the
|
||||
* given output stack array. */
|
||||
GPUNodeStack &GPU_node_get_output(const bNode &node, GPUNodeStack outputs[], StringRef identifier);
|
||||
|
||||
/* Returns the GPU node link of the input with the given identifier in the given node within the
|
||||
* given inputs stack array, if the input is not linked, a uniform link carrying the value of the
|
||||
* input will be created and returned. It is expected that the caller will use the returned link in
|
||||
* a GPU material, otherwise, the link may not be properly freed. */
|
||||
GPUNodeLink *GPU_node_get_input_link(const bNode &node,
|
||||
GPUNodeStack inputs[],
|
||||
StringRef identifier);
|
||||
|
||||
} // namespace blender
|
||||
231
blender-5.2.0/source/blender/gpu/GPU_matrix.hh
Normal file
231
blender-5.2.0/source/blender/gpu/GPU_matrix.hh
Normal file
@@ -0,0 +1,231 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Shader;
|
||||
} // namespace gpu
|
||||
|
||||
/**
|
||||
* To Identity transform & empty stack.
|
||||
*/
|
||||
void GPU_matrix_reset();
|
||||
|
||||
/* ModelView Matrix (2D or 3D) */
|
||||
|
||||
/**
|
||||
* Push a new matrix unto the stack (copying the previous matrix).
|
||||
*
|
||||
* \note A function `GPU_matrix_push_identity` could be added
|
||||
* instead of calling #GPU_matrix_identity_set after pushing.
|
||||
*/
|
||||
void GPU_matrix_push();
|
||||
void GPU_matrix_pop();
|
||||
|
||||
/**
|
||||
* Set the model-view to an identity matrix.
|
||||
*/
|
||||
void GPU_matrix_identity_set();
|
||||
|
||||
/**
|
||||
* Scale the model-view matrix.
|
||||
*/
|
||||
void GPU_matrix_scale_1f(float factor);
|
||||
|
||||
/* 3D ModelView Matrix */
|
||||
|
||||
void GPU_matrix_set(const float m[4][4]);
|
||||
void GPU_matrix_mul(const float m[4][4]);
|
||||
|
||||
void GPU_matrix_translate_3f(float x, float y, float z);
|
||||
void GPU_matrix_translate_3fv(const float vec[3]);
|
||||
void GPU_matrix_scale_3f(float x, float y, float z);
|
||||
void GPU_matrix_scale_3fv(const float vec[3]);
|
||||
|
||||
/**
|
||||
* Axis of rotation should be a unit vector.
|
||||
*/
|
||||
void GPU_matrix_rotate_3f(float deg, float x, float y, float z);
|
||||
/**
|
||||
* Axis of rotation should be a unit vector.
|
||||
*/
|
||||
void GPU_matrix_rotate_3fv(float deg, const float axis[3]);
|
||||
|
||||
void GPU_matrix_rotate_axis(float deg, char axis); /* TODO: enum for axis? */
|
||||
|
||||
void GPU_matrix_look_at(float eyeX,
|
||||
float eyeY,
|
||||
float eyeZ,
|
||||
float centerX,
|
||||
float centerY,
|
||||
float centerZ,
|
||||
float upX,
|
||||
float upY,
|
||||
float upZ);
|
||||
/* TODO: variant that takes eye[3], center[3], up[3] */
|
||||
|
||||
/* 2D ModelView Matrix */
|
||||
|
||||
void GPU_matrix_translate_2f(float x, float y);
|
||||
void GPU_matrix_translate_2fv(const float vec[2]);
|
||||
void GPU_matrix_scale_2f(float x, float y);
|
||||
void GPU_matrix_scale_2fv(const float vec[2]);
|
||||
void GPU_matrix_rotate_2d(float deg);
|
||||
|
||||
/* Projection Matrix (2D or 3D). */
|
||||
|
||||
void GPU_matrix_push_projection();
|
||||
void GPU_matrix_pop_projection();
|
||||
|
||||
/* 3D Projection Matrix. */
|
||||
|
||||
void GPU_matrix_identity_projection_set();
|
||||
void GPU_matrix_projection_set(const float m[4][4]);
|
||||
|
||||
void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far);
|
||||
void GPU_matrix_ortho_set_z(float near, float far);
|
||||
|
||||
void GPU_matrix_frustum_set(
|
||||
float left, float right, float bottom, float top, float near, float far);
|
||||
void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far);
|
||||
|
||||
/* 3D Projection between Window and World Space */
|
||||
|
||||
struct GPUMatrixUnproject_Precalc {
|
||||
float model_inverted[4][4];
|
||||
float view[4];
|
||||
bool is_persp;
|
||||
/**
|
||||
* Result of #projmat_dimensions_db.
|
||||
* Using double precision here is important as far clipping ranges
|
||||
* can cause divide-by-zero when using float, see: #66937.
|
||||
*/
|
||||
struct {
|
||||
double xmin, xmax;
|
||||
double ymin, ymax;
|
||||
double zmin, zmax;
|
||||
} dims;
|
||||
};
|
||||
|
||||
bool GPU_matrix_unproject_precalc(GPUMatrixUnproject_Precalc *unproj_precalc,
|
||||
const float model[4][4],
|
||||
const float proj[4][4],
|
||||
const int view[4]);
|
||||
|
||||
void GPU_matrix_project_3fv(const float world[3],
|
||||
const float model[4][4],
|
||||
const float proj[4][4],
|
||||
const int view[4],
|
||||
float r_win[3]);
|
||||
|
||||
void GPU_matrix_project_2fv(const float world[3],
|
||||
const float model[4][4],
|
||||
const float proj[4][4],
|
||||
const int view[4],
|
||||
float r_win[2]);
|
||||
|
||||
bool GPU_matrix_unproject_3fv(const float win[3],
|
||||
const float model_inverted[4][4],
|
||||
const float proj[4][4],
|
||||
const int view[4],
|
||||
float r_world[3]);
|
||||
|
||||
/* 2D Projection Matrix. */
|
||||
|
||||
void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top);
|
||||
|
||||
/* Functions to get matrix values. */
|
||||
|
||||
const float (*GPU_matrix_model_view_get(float m[4][4]))[4];
|
||||
const float (*GPU_matrix_projection_get(float m[4][4]))[4];
|
||||
const float (*GPU_matrix_model_view_projection_get(float m[4][4]))[4];
|
||||
|
||||
const float (*GPU_matrix_normal_get(float m[3][3]))[3];
|
||||
const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3];
|
||||
|
||||
/**
|
||||
* Set uniform values for currently bound shader.
|
||||
*/
|
||||
void GPU_matrix_bind(gpu::Shader *shader);
|
||||
bool GPU_matrix_dirty_get(); /* since last bind */
|
||||
|
||||
/**
|
||||
* Own working polygon offset.
|
||||
*/
|
||||
float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float dist);
|
||||
/**
|
||||
* \note \a viewdist is only for orthographic projections at the moment.
|
||||
*/
|
||||
void GPU_polygon_offset(float viewdist, float dist);
|
||||
|
||||
/* Python API needs to be able to inspect the stack so errors raise exceptions
|
||||
* instead of crashing. */
|
||||
#ifdef USE_GPU_PY_MATRIX_API
|
||||
int GPU_matrix_stack_level_get_model_view();
|
||||
int GPU_matrix_stack_level_get_projection();
|
||||
/* static assert ensures this doesn't change! */
|
||||
# define GPU_PY_MATRIX_STACK_LEN 31
|
||||
#endif /* USE_GPU_PY_MATRIX_API */
|
||||
|
||||
#ifndef SUPPRESS_GENERIC_MATRIX_API
|
||||
|
||||
# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
|
||||
# define _GPU_MAT3_CONST_CAST(x) \
|
||||
(_Generic((x), \
|
||||
void *: (const float (*)[3])(x), \
|
||||
float *: (const float (*)[3])(x), \
|
||||
float (*)[4]: (const float (*)[3])(x), \
|
||||
const void *: (const float (*)[3])(x), \
|
||||
const float *: (const float (*)[3])(x), \
|
||||
const float (*)[3]: (const float (*)[3])(x)))
|
||||
# define _GPU_MAT3_CAST(x) \
|
||||
(_Generic((x), \
|
||||
void *: (float (*)[3])(x), \
|
||||
float *: (float (*)[3])(x), \
|
||||
float (*)[3]: (float (*)[3])(x)))
|
||||
# define _GPU_MAT4_CONST_CAST(x) \
|
||||
(_Generic((x), \
|
||||
void *: (const float (*)[4])(x), \
|
||||
float *: (const float (*)[4])(x), \
|
||||
float (*)[4]: (const float (*)[4])(x), \
|
||||
const void *: (const float (*)[4])(x), \
|
||||
const float *: (const float (*)[4])(x), \
|
||||
const float (*)[4]: (const float (*)[4])(x)))
|
||||
# define _GPU_MAT4_CAST(x) \
|
||||
(_Generic((x), \
|
||||
void *: (float (*)[4])(x), \
|
||||
float *: (float (*)[4])(x), \
|
||||
float (*)[4]: (float (*)[4])(x)))
|
||||
# else
|
||||
# define _GPU_MAT3_CONST_CAST(x) (const float (*)[3])(x)
|
||||
# define _GPU_MAT3_CAST(x) (float (*)[3])(x)
|
||||
# define _GPU_MAT4_CONST_CAST(x) (const float (*)[4])(x)
|
||||
# define _GPU_MAT4_CAST(x) (float (*)[4])(x)
|
||||
# endif /* C11 */
|
||||
|
||||
/* make matrix inputs generic, to avoid warnings */
|
||||
# define GPU_matrix_mul(x) GPU_matrix_mul(_GPU_MAT4_CONST_CAST(x))
|
||||
# define GPU_matrix_set(x) GPU_matrix_set(_GPU_MAT4_CONST_CAST(x))
|
||||
# define GPU_matrix_projection_set(x) GPU_matrix_projection_set(_GPU_MAT4_CONST_CAST(x))
|
||||
# define GPU_matrix_model_view_get(x) GPU_matrix_model_view_get(_GPU_MAT4_CAST(x))
|
||||
# define GPU_matrix_projection_get(x) GPU_matrix_projection_get(_GPU_MAT4_CAST(x))
|
||||
# define GPU_matrix_model_view_projection_get(x) \
|
||||
GPU_matrix_model_view_projection_get(_GPU_MAT4_CAST(x))
|
||||
# define GPU_matrix_normal_get(x) GPU_matrix_normal_get(_GPU_MAT3_CAST(x))
|
||||
# define GPU_matrix_normal_inverse_get(x) GPU_matrix_normal_inverse_get(_GPU_MAT3_CAST(x))
|
||||
#endif /* SUPPRESS_GENERIC_MATRIX_API */
|
||||
|
||||
/* Not part of the GPU_matrix API,
|
||||
* however we need to check these limits in code that calls into these API's. */
|
||||
constexpr static int GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT = -100;
|
||||
constexpr static int GPU_MATRIX_ORTHO_CLIP_FAR_DEFAULT = 100;
|
||||
|
||||
} // namespace blender
|
||||
52
blender-5.2.0/source/blender/gpu/GPU_pass.hh
Normal file
52
blender-5.2.0/source/blender/gpu/GPU_pass.hh
Normal file
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Generate and cache shaders generated from the intermediate node graph.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUNodeGraph;
|
||||
|
||||
struct GPUPass;
|
||||
|
||||
enum GPUPassStatus {
|
||||
GPU_PASS_FAILED = 0,
|
||||
GPU_PASS_QUEUED,
|
||||
GPU_PASS_SUCCESS,
|
||||
};
|
||||
|
||||
GPUPass *GPU_generate_pass(GPUMaterial *material,
|
||||
GPUNodeGraph *graph,
|
||||
const char *debug_name,
|
||||
eGPUMaterialEngine engine,
|
||||
bool deferred_compilation,
|
||||
GPUCodegenCallbackFn finalize_source_cb,
|
||||
void *thunk,
|
||||
bool optimize_graph);
|
||||
|
||||
GPUPassStatus GPU_pass_status(GPUPass *pass);
|
||||
bool GPU_pass_should_optimize(GPUPass *pass);
|
||||
void GPU_pass_ensure_its_ready(GPUPass *pass);
|
||||
gpu::Shader *GPU_pass_shader_get(GPUPass *pass);
|
||||
void GPU_pass_acquire(GPUPass *pass);
|
||||
void GPU_pass_release(GPUPass *pass);
|
||||
|
||||
uint64_t GPU_pass_global_compilation_count();
|
||||
uint64_t GPU_pass_compilation_timestamp(GPUPass *pass);
|
||||
|
||||
void GPU_pass_cache_init();
|
||||
void GPU_pass_cache_update();
|
||||
void GPU_pass_cache_wait_for_all();
|
||||
void GPU_pass_cache_free();
|
||||
|
||||
} // namespace blender
|
||||
106
blender-5.2.0/source/blender/gpu/GPU_platform.hh
Normal file
106
blender-5.2.0/source/blender/gpu/GPU_platform.hh
Normal file
@@ -0,0 +1,106 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "GPU_platform_backend_enum.h" // IWYU pragma: export
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* GPU platform support */
|
||||
|
||||
/* GPU Types */
|
||||
enum GPUDeviceType {
|
||||
GPU_DEVICE_NVIDIA = (1 << 0),
|
||||
GPU_DEVICE_ATI = (1 << 1),
|
||||
GPU_DEVICE_INTEL = (1 << 2),
|
||||
GPU_DEVICE_INTEL_UHD = (1 << 3),
|
||||
GPU_DEVICE_APPLE = (1 << 4),
|
||||
GPU_DEVICE_SOFTWARE = (1 << 5),
|
||||
GPU_DEVICE_QUALCOMM = (1 << 6),
|
||||
GPU_DEVICE_UNKNOWN = (1 << 7),
|
||||
GPU_DEVICE_ANY = (0xff),
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUDeviceType)
|
||||
|
||||
enum GPUOSType {
|
||||
GPU_OS_WIN = (1 << 8),
|
||||
GPU_OS_MAC = (1 << 9),
|
||||
GPU_OS_UNIX = (1 << 10),
|
||||
GPU_OS_ANY = (0xff00),
|
||||
};
|
||||
|
||||
enum GPUDriverType {
|
||||
GPU_DRIVER_OFFICIAL = (1 << 16),
|
||||
GPU_DRIVER_OPENSOURCE = (1 << 17),
|
||||
GPU_DRIVER_SOFTWARE = (1 << 18),
|
||||
GPU_DRIVER_ANY = (0xff0000),
|
||||
};
|
||||
|
||||
enum GPUSupportLevel {
|
||||
GPU_SUPPORT_LEVEL_SUPPORTED,
|
||||
GPU_SUPPORT_LEVEL_LIMITED,
|
||||
GPU_SUPPORT_LEVEL_UNSUPPORTED,
|
||||
};
|
||||
|
||||
enum GPUArchitectureType {
|
||||
/* Immediate Mode Renderer (IMR).
|
||||
* Typically, an IMR architecture will execute GPU work in sequence, rasterizing primitives in
|
||||
* order. */
|
||||
GPU_ARCHITECTURE_IMR = 0,
|
||||
|
||||
/* Tile-Based-Deferred-Renderer (TBDR).
|
||||
* A TBDR architecture will typically execute the vertex stage up-front for all primitives,
|
||||
* binning geometry into distinct tiled regions. Fragments will then be rasterized within
|
||||
* the bounds of one tile at a time. */
|
||||
GPU_ARCHITECTURE_TBDR = 1,
|
||||
};
|
||||
|
||||
struct GPUDevice {
|
||||
std::string identifier;
|
||||
int index;
|
||||
uint32_t vendor_id;
|
||||
uint32_t device_id;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/* GPU Types */
|
||||
/* TODO: Verify all use-cases of GPU_type_matches to determine which graphics API it should apply
|
||||
* to, and replace with `GPU_type_matches_ex` where appropriate. */
|
||||
bool GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver);
|
||||
bool GPU_type_matches_ex(GPUDeviceType device,
|
||||
GPUOSType os,
|
||||
GPUDriverType driver,
|
||||
GPUBackendType backend);
|
||||
|
||||
GPUSupportLevel GPU_platform_support_level();
|
||||
const char *GPU_platform_vendor();
|
||||
const char *GPU_platform_renderer();
|
||||
const char *GPU_platform_version();
|
||||
const char *GPU_platform_support_level_key();
|
||||
const char *GPU_platform_gpu_name();
|
||||
GPUArchitectureType GPU_platform_architecture();
|
||||
Span<GPUDevice> GPU_platform_devices_list();
|
||||
|
||||
/* The UUID of the device. Can be an empty array, since it is not supported on all platforms. */
|
||||
Span<uint8_t> GPU_platform_uuid();
|
||||
/* The LUID of the device. Can be an empty array, since it is not supported on all platforms. */
|
||||
Span<uint8_t> GPU_platform_luid();
|
||||
/* A bit field with the nth bit active identifying the nth device with the same LUID. Only matters
|
||||
* if LUID is defined. */
|
||||
uint32_t GPU_platform_luid_node_mask();
|
||||
|
||||
} // namespace blender
|
||||
22
blender-5.2.0/source/blender/gpu/GPU_platform_backend_enum.h
Normal file
22
blender-5.2.0/source/blender/gpu/GPU_platform_backend_enum.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** NOTE: Keep in sync with eUserPref_GPUBackendType. */
|
||||
enum GPUBackendType {
|
||||
GPU_BACKEND_NONE = 0,
|
||||
GPU_BACKEND_OPENGL = 1 << 0,
|
||||
GPU_BACKEND_METAL = 1 << 1,
|
||||
GPU_BACKEND_VULKAN = 1 << 3,
|
||||
GPU_BACKEND_ANY = 0xFFFFFFFFu
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
124
blender-5.2.0/source/blender/gpu/GPU_primitive.hh
Normal file
124
blender-5.2.0/source/blender/gpu/GPU_primitive.hh
Normal file
@@ -0,0 +1,124 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU geometric primitives
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
enum GPUPrimType : int8_t {
|
||||
GPU_PRIM_POINTS,
|
||||
GPU_PRIM_LINES,
|
||||
GPU_PRIM_TRIS,
|
||||
GPU_PRIM_LINE_STRIP,
|
||||
GPU_PRIM_LINE_LOOP, /* GL has this, Vulkan and Metal do not */
|
||||
GPU_PRIM_TRI_STRIP,
|
||||
GPU_PRIM_TRI_FAN, /* Metal API does not support this. */
|
||||
|
||||
/* Metal API does not support ADJ primitive types but
|
||||
* handled via the geometry-shader-alternative path. */
|
||||
GPU_PRIM_LINES_ADJ,
|
||||
GPU_PRIM_TRIS_ADJ,
|
||||
GPU_PRIM_LINE_STRIP_ADJ,
|
||||
|
||||
GPU_PRIM_NONE,
|
||||
};
|
||||
|
||||
/* what types of primitives does each shader expect? */
|
||||
enum GPUPimClass {
|
||||
GPU_PRIM_CLASS_NONE = 0,
|
||||
GPU_PRIM_CLASS_POINT = (1 << 0),
|
||||
GPU_PRIM_CLASS_LINE = (1 << 1),
|
||||
GPU_PRIM_CLASS_SURFACE = (1 << 2),
|
||||
GPU_PRIM_CLASS_ANY = GPU_PRIM_CLASS_POINT | GPU_PRIM_CLASS_LINE | GPU_PRIM_CLASS_SURFACE,
|
||||
};
|
||||
|
||||
inline int gpu_get_prim_count_from_type(uint vertex_len, GPUPrimType prim_type)
|
||||
{
|
||||
/* does vertex_len make sense for this primitive type? */
|
||||
if (vertex_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (prim_type) {
|
||||
case GPU_PRIM_POINTS:
|
||||
return vertex_len;
|
||||
|
||||
case GPU_PRIM_LINES:
|
||||
BLI_assert(vertex_len % 2 == 0);
|
||||
return vertex_len / 2;
|
||||
|
||||
case GPU_PRIM_LINE_STRIP:
|
||||
return vertex_len - 1;
|
||||
|
||||
case GPU_PRIM_LINE_LOOP:
|
||||
return vertex_len;
|
||||
|
||||
case GPU_PRIM_LINES_ADJ:
|
||||
BLI_assert(vertex_len % 4 == 0);
|
||||
return vertex_len / 4;
|
||||
|
||||
case GPU_PRIM_LINE_STRIP_ADJ:
|
||||
return vertex_len - 2;
|
||||
|
||||
case GPU_PRIM_TRIS:
|
||||
BLI_assert(vertex_len % 3 == 0);
|
||||
return vertex_len / 3;
|
||||
|
||||
case GPU_PRIM_TRI_STRIP:
|
||||
BLI_assert(vertex_len >= 3);
|
||||
return vertex_len - 2;
|
||||
|
||||
case GPU_PRIM_TRI_FAN:
|
||||
BLI_assert(vertex_len >= 3);
|
||||
return vertex_len - 2;
|
||||
|
||||
case GPU_PRIM_TRIS_ADJ:
|
||||
BLI_assert(vertex_len % 6 == 0);
|
||||
return vertex_len / 6;
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_restart_compatible(GPUPrimType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_PRIM_POINTS:
|
||||
case GPU_PRIM_LINES:
|
||||
case GPU_PRIM_TRIS:
|
||||
case GPU_PRIM_LINES_ADJ:
|
||||
case GPU_PRIM_TRIS_ADJ:
|
||||
case GPU_PRIM_NONE:
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
case GPU_PRIM_LINE_STRIP:
|
||||
case GPU_PRIM_LINE_LOOP:
|
||||
case GPU_PRIM_TRI_STRIP:
|
||||
case GPU_PRIM_TRI_FAN:
|
||||
case GPU_PRIM_LINE_STRIP_ADJ: {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Improve error checking by validating that the shader is suited for this primitive type.
|
||||
* GPUPrimClass GPU_primtype_class(GPUPrimType);
|
||||
* bool GPU_primtype_belongs_to_class(GPUPrimType, GPUPrimClass);
|
||||
*/
|
||||
|
||||
} // namespace blender
|
||||
106
blender-5.2.0/source/blender/gpu/GPU_select.hh
Normal file
106
blender-5.2.0/source/blender/gpu/GPU_select.hh
Normal file
@@ -0,0 +1,106 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct rcti;
|
||||
|
||||
/** Flags for mode of operation. */
|
||||
enum GPUSelectMode {
|
||||
GPU_SELECT_INVALID = 0,
|
||||
GPU_SELECT_ALL = 1,
|
||||
/* gpu_select_query */
|
||||
GPU_SELECT_NEAREST_FIRST_PASS = 2,
|
||||
GPU_SELECT_NEAREST_SECOND_PASS = 3,
|
||||
/* gpu_select_pick */
|
||||
GPU_SELECT_PICK_ALL = 4,
|
||||
GPU_SELECT_PICK_NEAREST = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* The result of calling #GPU_select_begin & #GPU_select_end.
|
||||
*/
|
||||
struct GPUSelectResult {
|
||||
/** The selection identifier matching the value passed in by #GPU_select_load_id. */
|
||||
unsigned int id;
|
||||
/**
|
||||
* The nearest depth.
|
||||
* - Only supported by picking modes (#GPU_SELECT_PICK_ALL and #GPU_SELECT_PICK_NEAREST)
|
||||
* since occlusion quires don't provide a convenient way of accessing the depth-buffer.
|
||||
* - OpenGL's `GL_SELECT` supported both near and far depths,
|
||||
* this has not been included as Blender doesn't need this however support could be added.
|
||||
*/
|
||||
unsigned int depth;
|
||||
};
|
||||
|
||||
using GPUSelectStorage = Vector<GPUSelectResult, 2500>;
|
||||
struct GPUSelectBuffer {
|
||||
GPUSelectStorage storage;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize and provide buffer for results.
|
||||
*/
|
||||
void GPU_select_begin(GPUSelectBuffer *buffer, const rcti *input, GPUSelectMode mode, int oldhits);
|
||||
/**
|
||||
* Initialize and provide buffer for results.
|
||||
* Uses the new Select-Next engine if enabled.
|
||||
*/
|
||||
void GPU_select_begin_next(
|
||||
GPUSelectBuffer *buffer, const rcti *input, int radius, GPUSelectMode mode, int oldhits);
|
||||
/**
|
||||
* Loads a new selection id and ends previous query, if any.
|
||||
* In second pass of selection it also returns
|
||||
* if id has been hit on the first pass already.
|
||||
* Thus we can skip drawing un-hit objects.
|
||||
*
|
||||
* \warning We rely on the order of object rendering on passes to be the same for this to work.
|
||||
*/
|
||||
bool GPU_select_load_id(unsigned int id);
|
||||
void GPU_select_finalize();
|
||||
/**
|
||||
* Cleanup and flush selection results to buffer.
|
||||
* Return number of hits and hits in buffer.
|
||||
* if \a dopass is true, we will do a second pass with occlusion queries to get the closest hit.
|
||||
*/
|
||||
unsigned int GPU_select_end();
|
||||
|
||||
/* Cache selection region. */
|
||||
|
||||
bool GPU_select_is_cached();
|
||||
void GPU_select_cache_begin();
|
||||
void GPU_select_cache_load_id();
|
||||
void GPU_select_cache_end();
|
||||
|
||||
/* Utilities. */
|
||||
|
||||
/**
|
||||
* Helper function, nothing special but avoids doing inline since hits aren't sorted by depth
|
||||
* and purpose of 4x buffer indices isn't so clear.
|
||||
*
|
||||
* Note that comparing depth as uint is fine.
|
||||
*/
|
||||
const GPUSelectResult *GPU_select_buffer_near(const Span<GPUSelectResult> hit_results);
|
||||
/**
|
||||
* Compare result of `GPU_select`: #GPUSelectResult,
|
||||
* Needed for stable sorting, so cycling through all items near the cursor behaves predictably.
|
||||
*/
|
||||
int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p);
|
||||
uint GPU_select_buffer_remove_by_id(MutableSpan<GPUSelectResult> hit_results, uint select_id);
|
||||
/**
|
||||
* Part of the solution copied from `rect_subregion_stride_calc`.
|
||||
*/
|
||||
void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_buf);
|
||||
|
||||
} // namespace blender
|
||||
585
blender-5.2.0/source/blender/gpu/GPU_shader.hh
Normal file
585
blender-5.2.0/source/blender/gpu/GPU_shader.hh
Normal file
@@ -0,0 +1,585 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* A #gpu::Shader is a container for backend specific shader program.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_common_types.hh"
|
||||
#include "GPU_shader_builtin.hh"
|
||||
|
||||
struct GPUShaderCreateInfo;
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class VertBuf;
|
||||
class Shader;
|
||||
namespace shader {
|
||||
struct ShaderCreateInfo;
|
||||
} // namespace shader
|
||||
} // namespace gpu
|
||||
|
||||
/** Opaque type hiding #gpu::shader::ShaderCreateInfo */
|
||||
/* Hardware limit is 16. Position attribute is always needed so we reduce to 15.
|
||||
* This makes sure the GPUVertexFormat name buffer does not overflow. */
|
||||
constexpr static int GPU_MAX_ATTR = 15;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Preprocess a raw GLSL source to adhere to our backend compatible shader language.
|
||||
* Needed if the string was not part of our build system and is used in a #GPUShaderCreateInfo.
|
||||
* `info` is the create info to add builtin flags parsed from the original string.
|
||||
*/
|
||||
std::string GPU_shader_preprocess_source(StringRefNull original,
|
||||
gpu::shader::ShaderCreateInfo &info);
|
||||
|
||||
/**
|
||||
* Create a shader using the given #GPUShaderCreateInfo.
|
||||
* Can return a null pointer if compilation fails.
|
||||
*/
|
||||
gpu::Shader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info);
|
||||
|
||||
/**
|
||||
* Same as GPU_shader_create_from_info but will run preprocessor on source strings.
|
||||
*/
|
||||
gpu::Shader *GPU_shader_create_from_info_python(const GPUShaderCreateInfo *_info);
|
||||
|
||||
/**
|
||||
* Create a shader using a named #GPUShaderCreateInfo registered at startup.
|
||||
* These are declared inside `*_info.hh` files using the `GPU_SHADER_CREATE_INFO()` macro.
|
||||
* They are also expected to have been flagged using `do_static_compilation`.
|
||||
* Can return a null pointer if compilation fails.
|
||||
*/
|
||||
gpu::Shader *GPU_shader_create_from_info_name(const char *info_name);
|
||||
|
||||
/**
|
||||
* Fetch a named #GPUShaderCreateInfo registered at startup.
|
||||
* These are declared inside `*_info.hh` files using the `GPU_SHADER_CREATE_INFO()` macro.
|
||||
* Can return a null pointer if no match is found.
|
||||
*/
|
||||
const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name);
|
||||
|
||||
/**
|
||||
* Error checking for user created shaders.
|
||||
* \return true is create info is valid.
|
||||
*/
|
||||
bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128]);
|
||||
|
||||
enum class CompilationPriority { Low, Medium, High };
|
||||
|
||||
using AsyncCompilationHandle = int64_t;
|
||||
/**
|
||||
* Request the async compilation of a shader, allowing the backend to use multithreaded
|
||||
* compilation. Returns a handle that can be used to poll if the shader has been compiled, and to
|
||||
* retrieve the compiled shader.
|
||||
* WARNING: The GPUShaderCreateInfo pointer should be valid until
|
||||
* `GPU_shader_async_compilation_finalize` has returned.
|
||||
*/
|
||||
AsyncCompilationHandle GPU_shader_async_compilation(
|
||||
const GPUShaderCreateInfo *info, CompilationPriority priority = CompilationPriority::High);
|
||||
/**
|
||||
* Returns true if the shader has already been compiled.
|
||||
*/
|
||||
bool GPU_shader_async_compilation_is_ready(AsyncCompilationHandle handle);
|
||||
/**
|
||||
* Retrieve the compiled shader.
|
||||
* If the compilation has not finished yet, this call will block the thread until the shader is
|
||||
* ready. Shaders with compilation errors are returned as null pointers.
|
||||
* WARNING: The handle will be invalidated by this call, you can't request the same shader twice.
|
||||
*/
|
||||
gpu::Shader *GPU_shader_async_compilation_finalize(AsyncCompilationHandle &handle);
|
||||
/**
|
||||
* Cancel the compilation of the shader.
|
||||
* WARNING: The handle will be invalidated by this call.
|
||||
*/
|
||||
void GPU_shader_async_compilation_cancel(AsyncCompilationHandle &handle);
|
||||
|
||||
/**
|
||||
* Returns true if there's any shader still being compiled.
|
||||
* NOTE: This returns true as long as there are batches in the compilation queue.
|
||||
* It doesn't take into account if compilation is paused.
|
||||
*/
|
||||
bool GPU_shader_compiler_has_pending_work();
|
||||
/**
|
||||
* Wait until all the requested shaders have been compiled.
|
||||
*/
|
||||
void GPU_shader_compiler_wait_for_all();
|
||||
/**
|
||||
* Pauses all shader compilations.
|
||||
*/
|
||||
void GPU_shader_compiler_pause();
|
||||
/**
|
||||
* Resumes compilations after being paused.
|
||||
*/
|
||||
void GPU_shader_compiler_resume();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Free
|
||||
* \{ */
|
||||
|
||||
void GPU_shader_free(gpu::Shader *shader);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Binding
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Set the given shader as active shader for the active GPU context.
|
||||
* It replaces any already bound shader.
|
||||
* All following draw-calls and dispatches will use this shader.
|
||||
* Uniform functions need to have the shader bound in order to work. (TODO: until we use
|
||||
* glProgramUniform)
|
||||
*/
|
||||
void GPU_shader_bind(gpu::Shader *shader,
|
||||
const gpu::shader::SpecializationConstants *constants_state = nullptr);
|
||||
|
||||
/**
|
||||
* Unbind the active shader.
|
||||
* \note this is a no-op in release builds. But it make sense to actually do it in user land code
|
||||
* to detect incorrect API usage.
|
||||
*/
|
||||
void GPU_shader_unbind();
|
||||
|
||||
/**
|
||||
* Return the currently bound shader to the active GPU context.
|
||||
* \return null pointer if no shader is bound of if no context is active.
|
||||
*/
|
||||
gpu::Shader *GPU_shader_get_bound();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debugging introspection API.
|
||||
* \{ */
|
||||
|
||||
const char *GPU_shader_get_name(gpu::Shader *shader);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Uniform API.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns binding point location.
|
||||
* Binding location are given to be set at shader compile time and immutable.
|
||||
*/
|
||||
int GPU_shader_get_ubo_binding(gpu::Shader *shader, const char *name);
|
||||
int GPU_shader_get_ssbo_binding(gpu::Shader *shader, const char *name);
|
||||
int GPU_shader_get_sampler_binding(gpu::Shader *shader, const char *name);
|
||||
|
||||
/**
|
||||
* Returns uniform location.
|
||||
* If cached, it is faster than querying the interface for each uniform assignment.
|
||||
*/
|
||||
int GPU_shader_get_uniform(gpu::Shader *shader, const char *name);
|
||||
|
||||
/**
|
||||
* Returns specialization constant location.
|
||||
*/
|
||||
int GPU_shader_get_constant(gpu::Shader *shader, const char *name);
|
||||
|
||||
/**
|
||||
* Sets a generic push constant (a.k.a. uniform).
|
||||
* \a length and \a array_size should match the create info push_constant declaration.
|
||||
*/
|
||||
void GPU_shader_uniform_float_ex(
|
||||
gpu::Shader *shader, int location, int length, int array_size, const float *value);
|
||||
void GPU_shader_uniform_int_ex(
|
||||
gpu::Shader *shader, int location, int length, int array_size, const int *value);
|
||||
|
||||
/**
|
||||
* Sets a generic push constant (a.k.a. uniform).
|
||||
* \a length and \a array_size should match the create info push_constant declaration.
|
||||
* These functions need to have the shader bound in order to work. (TODO: until we use
|
||||
* glProgramUniform)
|
||||
*/
|
||||
void GPU_shader_uniform_1i(gpu::Shader *sh, const char *name, int value);
|
||||
void GPU_shader_uniform_1b(gpu::Shader *sh, const char *name, bool value);
|
||||
void GPU_shader_uniform_1f(gpu::Shader *sh, const char *name, float value);
|
||||
void GPU_shader_uniform_2f(gpu::Shader *sh, const char *name, float x, float y);
|
||||
void GPU_shader_uniform_3f(gpu::Shader *sh, const char *name, float x, float y, float z);
|
||||
void GPU_shader_uniform_4f(gpu::Shader *sh, const char *name, float x, float y, float z, float w);
|
||||
void GPU_shader_uniform_2fv(gpu::Shader *sh, const char *name, const float data[2]);
|
||||
void GPU_shader_uniform_3fv(gpu::Shader *sh, const char *name, const float data[3]);
|
||||
void GPU_shader_uniform_4fv(gpu::Shader *sh, const char *name, const float data[4]);
|
||||
void GPU_shader_uniform_2iv(gpu::Shader *sh, const char *name, const int data[2]);
|
||||
void GPU_shader_uniform_3iv(gpu::Shader *sh, const char *name, const int data[3]);
|
||||
void GPU_shader_uniform_mat4(gpu::Shader *sh, const char *name, const float data[4][4]);
|
||||
void GPU_shader_uniform_mat3(gpu::Shader *sh, const char *name, const float data[3][3]);
|
||||
void GPU_shader_uniform_mat3_as_mat4(gpu::Shader *sh, const char *name, const float data[3][3]);
|
||||
void GPU_shader_uniform_1f_array(gpu::Shader *sh, const char *name, int len, const float *val);
|
||||
void GPU_shader_uniform_2fv_array(gpu::Shader *sh,
|
||||
const char *name,
|
||||
int len,
|
||||
const float (*val)[2]);
|
||||
void GPU_shader_uniform_4fv_array(gpu::Shader *sh,
|
||||
const char *name,
|
||||
int len,
|
||||
const float (*val)[4]);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Attribute API.
|
||||
*
|
||||
* Used to create #GPUVertexFormat from the shader's vertex input layout.
|
||||
* \{ */
|
||||
|
||||
uint GPU_shader_get_attribute_len(const gpu::Shader *shader);
|
||||
uint GPU_shader_get_ssbo_input_len(const gpu::Shader *shader);
|
||||
int GPU_shader_get_attribute(const gpu::Shader *shader, const char *name);
|
||||
bool GPU_shader_get_attribute_info(const gpu::Shader *shader,
|
||||
int attr_location,
|
||||
char r_name[256],
|
||||
int *r_type);
|
||||
bool GPU_shader_get_ssbo_input_info(const gpu::Shader *shader,
|
||||
int ssbo_location,
|
||||
char r_name[256]);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Specialization API.
|
||||
*
|
||||
* Used to allow specialization constants.
|
||||
* IMPORTANT: All constants must be specified before binding a shader that needs specialization.
|
||||
* Otherwise, it will produce undefined behavior.
|
||||
* \{ */
|
||||
|
||||
/* Return the default constants.
|
||||
* All constants available for this shader should fit the returned structure. */
|
||||
const gpu::shader::SpecializationConstants &GPU_shader_get_default_constant_state(gpu::Shader *sh);
|
||||
|
||||
using AsyncSpecializationHandle = int64_t;
|
||||
|
||||
struct ShaderSpecialization {
|
||||
gpu::Shader *shader;
|
||||
gpu::shader::SpecializationConstants constants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request the async compilation of a shader specialization, allowing the backend to use
|
||||
* multi-threaded compilation.
|
||||
* Returns a handle that can be used to poll if the compilation has finished.
|
||||
* A NULL handle indicates no compilation of any variant was possible (likely due to
|
||||
* some state being currently available) and so no batch was created. Compilation
|
||||
* of the specialized variant will instead occur at draw/dispatch time.
|
||||
* NOTE: This function is asynchronous on OpenGL and Metal and a no-op on Vulkan.
|
||||
* Batches are processed one by one in FIFO order.
|
||||
* WARNING: Binding a specialization before the batch finishes will fail.
|
||||
*/
|
||||
AsyncSpecializationHandle GPU_shader_async_specialization(
|
||||
const ShaderSpecialization &specialization,
|
||||
CompilationPriority priority = CompilationPriority::High);
|
||||
|
||||
/**
|
||||
* Returns true if the specialization has finished its compilation.
|
||||
* WARNING: Invalidates the handle if it returns true.
|
||||
*/
|
||||
bool GPU_shader_async_specialization_is_ready(AsyncSpecializationHandle &handle);
|
||||
|
||||
/**
|
||||
* Cancel the specialization compilation.
|
||||
* WARNING: The handle will be invalidated by this call.
|
||||
*/
|
||||
void GPU_shader_async_specialization_cancel(AsyncSpecializationHandle &handle);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Legacy API
|
||||
*
|
||||
* All of this section is deprecated and should be ported to use the API described above.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Shader cache warming.
|
||||
* For each shader, rendering APIs perform a two-step compilation:
|
||||
*
|
||||
* * The first stage is Front-End compilation which only needs to be performed once, and generates
|
||||
* a portable intermediate representation. This happens during `gpu::Shader::finalize()`.
|
||||
*
|
||||
* * The second is Back-End compilation which compiles a device-specific executable shader
|
||||
* program. This compilation requires some contextual pipeline state which is baked into the
|
||||
* executable shader source, producing a Pipeline State Object (PSO). In OpenGL, backend
|
||||
* compilation happens in the background, within the driver, but can still incur runtime stutters.
|
||||
* In Metal/Vulkan, PSOs are compiled explicitly. These are currently resolved within the backend
|
||||
* based on the current pipeline state and can incur runtime stalls when they occur.
|
||||
*
|
||||
* Shader Cache warming uses the specified parent shader set using `GPU_shader_set_parent(..)` as a
|
||||
* template reference for pre-compiling Render Pipeline State Objects (PSOs) outside of the main
|
||||
* render pipeline.
|
||||
*
|
||||
* PSOs require descriptors containing information on the render state for a given shader, which
|
||||
* includes input vertex data layout and output pixel formats, along with some state such as
|
||||
* blend mode and color output masks. As this state information is usually consistent between
|
||||
* similar draws, we can assign a parent shader and use this shader's cached pipeline state's to
|
||||
* prime compilations.
|
||||
*
|
||||
* Shaders do not necessarily have to be similar in functionality to be used as a parent, so long
|
||||
* as the #GPUVertFormat and #gpu::FrameBuffer which they are used with remain the same.
|
||||
* Other bindings such as textures, uniforms and UBOs are all assigned independently as dynamic
|
||||
* state.
|
||||
*
|
||||
* This function should be called asynchronously, mitigating the impact of run-time stuttering from
|
||||
* dynamic compilation of PSOs during normal rendering.
|
||||
*
|
||||
* \param: shader: The shader whose cache to warm.
|
||||
* \param limit: The maximum number of PSOs to compile within a call. Specifying
|
||||
* a limit <= 0 will compile a PSO for all cached PSOs in the parent shader. */
|
||||
void GPU_shader_warm_cache(gpu::Shader *shader, int limit);
|
||||
|
||||
/* We expect the parent shader to be compiled and already have some cached PSOs when being assigned
|
||||
* as a reference. Ensure the parent shader still exists when `GPU_shader_cache_warm(..)` is
|
||||
* called. */
|
||||
void GPU_shader_set_parent(gpu::Shader *shader, gpu::Shader *parent);
|
||||
|
||||
/**
|
||||
* Indexed commonly used uniform name for faster lookup into the uniform cache.
|
||||
*/
|
||||
enum GPUUniformBuiltin {
|
||||
GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */
|
||||
GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */
|
||||
GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */
|
||||
GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */
|
||||
GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */
|
||||
GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */
|
||||
|
||||
GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */
|
||||
GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */
|
||||
GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */
|
||||
GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */
|
||||
GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */
|
||||
|
||||
GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */
|
||||
GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */
|
||||
|
||||
GPU_UNIFORM_COLOR, /* vec4 color */
|
||||
GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */
|
||||
GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */
|
||||
GPU_UNIFORM_SCENE_LINEAR_XFORM, /* float3x3 gpu_scene_linear_to_xyz */
|
||||
};
|
||||
#define GPU_NUM_UNIFORMS (GPU_UNIFORM_SCENE_LINEAR_XFORM + 1)
|
||||
|
||||
/**
|
||||
* TODO: To be moved as private API. Not really used outside of gpu_matrix.cc and doesn't really
|
||||
* offer a noticeable performance boost.
|
||||
*/
|
||||
int GPU_shader_get_builtin_uniform(gpu::Shader *shader, int builtin);
|
||||
|
||||
/**
|
||||
* Compile all statically defined shaders and print a report to the console.
|
||||
*
|
||||
* This is used for platform support, where bug reports can list all failing shaders.
|
||||
*/
|
||||
void GPU_shader_compile_static();
|
||||
|
||||
void GPU_shader_cache_dir_clear_old();
|
||||
|
||||
/** DEPRECATED: Use hard-coded buffer location instead. */
|
||||
enum GPUUniformBlockBuiltin {
|
||||
GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */
|
||||
GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */
|
||||
GPU_UNIFORM_BLOCK_INFO, /* infoBlock */
|
||||
|
||||
GPU_UNIFORM_BLOCK_DRW_VIEW,
|
||||
GPU_UNIFORM_BLOCK_DRW_MODEL,
|
||||
GPU_UNIFORM_BLOCK_DRW_INFOS,
|
||||
GPU_UNIFORM_BLOCK_DRW_CLIPPING,
|
||||
|
||||
GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
|
||||
};
|
||||
|
||||
/** DEPRECATED: Kept only because of Python GPU API. */
|
||||
int GPU_shader_get_uniform_block(gpu::Shader *shader, const char *name);
|
||||
|
||||
/** \} */
|
||||
|
||||
#define GPU_SHADER_FREE_SAFE(shader) \
|
||||
do { \
|
||||
if (shader != nullptr) { \
|
||||
GPU_shader_free(shader); \
|
||||
shader = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/* gpu::Shader wrapper that makes compilation threadsafe.
|
||||
* The compilation is deferred until the first get() call.
|
||||
* Concurrently using the shader from multiple threads is still unsafe. */
|
||||
class StaticShader : NonCopyable {
|
||||
private:
|
||||
std::string info_name_;
|
||||
std::atomic<gpu::Shader *> shader_ = nullptr;
|
||||
/* TODO: Failed compilation detection should be supported by the gpu::Shader API. */
|
||||
std::atomic<bool> failed_ = false;
|
||||
std::mutex mutex_;
|
||||
/* Handle for async compilation. */
|
||||
AsyncCompilationHandle compilation_handle_ = 0;
|
||||
|
||||
void move(StaticShader &&other)
|
||||
{
|
||||
std::scoped_lock lock1(mutex_);
|
||||
std::scoped_lock lock2(other.mutex_);
|
||||
BLI_assert(shader_ == nullptr && info_name_.empty());
|
||||
std::swap(info_name_, other.info_name_);
|
||||
/* No std::swap support for atomics. */
|
||||
shader_.exchange(other.shader_.exchange(shader_));
|
||||
failed_.exchange(other.failed_.exchange(failed_));
|
||||
std::swap(compilation_handle_, other.compilation_handle_);
|
||||
}
|
||||
|
||||
public:
|
||||
StaticShader(std::string info_name) : info_name_(info_name) {}
|
||||
|
||||
StaticShader() = default;
|
||||
StaticShader(StaticShader &&other)
|
||||
{
|
||||
move(std::move(other));
|
||||
}
|
||||
StaticShader &operator=(StaticShader &&other)
|
||||
{
|
||||
move(std::move(other));
|
||||
return *this;
|
||||
};
|
||||
|
||||
~StaticShader()
|
||||
{
|
||||
if (compilation_handle_) {
|
||||
GPU_shader_async_compilation_cancel(compilation_handle_);
|
||||
}
|
||||
GPU_SHADER_FREE_SAFE(shader_);
|
||||
}
|
||||
|
||||
/* Schedule the shader to be compile in a worker thread. */
|
||||
void ensure_compile_async()
|
||||
{
|
||||
if (is_ready()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lock(mutex_);
|
||||
|
||||
if (compilation_handle_) {
|
||||
if (GPU_shader_async_compilation_is_ready(compilation_handle_)) {
|
||||
shader_ = GPU_shader_async_compilation_finalize(compilation_handle_);
|
||||
failed_ = shader_ == nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shader_ && !failed_ && !compilation_handle_) {
|
||||
BLI_assert(!info_name_.empty());
|
||||
const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(info_name_.c_str());
|
||||
compilation_handle_ = GPU_shader_async_compilation(create_info);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_ready()
|
||||
{
|
||||
return shader_ || failed_;
|
||||
}
|
||||
|
||||
gpu::Shader *get()
|
||||
{
|
||||
if (is_ready()) {
|
||||
return shader_;
|
||||
}
|
||||
|
||||
std::scoped_lock lock(mutex_);
|
||||
|
||||
if (!shader_ && !failed_) {
|
||||
if (compilation_handle_) {
|
||||
shader_ = GPU_shader_async_compilation_finalize(compilation_handle_);
|
||||
}
|
||||
else {
|
||||
BLI_assert(!info_name_.empty());
|
||||
shader_ = GPU_shader_create_from_info_name(info_name_.c_str());
|
||||
}
|
||||
failed_ = shader_ == nullptr;
|
||||
}
|
||||
|
||||
return shader_;
|
||||
}
|
||||
|
||||
/* For batch compiled shaders. */
|
||||
/* TODO: Find a better way to handle this. */
|
||||
void set(gpu::Shader *shader)
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
BLI_assert(shader_ == nullptr);
|
||||
shader_ = shader;
|
||||
}
|
||||
};
|
||||
|
||||
/* Thread-safe container for StaticShader cache classes.
|
||||
* The class instance creation is deferred until the first get() call. */
|
||||
template<typename T> class StaticShaderCache {
|
||||
std::atomic<T *> cache_ = nullptr;
|
||||
std::mutex mutex_;
|
||||
|
||||
public:
|
||||
~StaticShaderCache()
|
||||
{
|
||||
BLI_assert(cache_ == nullptr);
|
||||
}
|
||||
|
||||
template<typename... Args> T &get(Args &&...constructor_args)
|
||||
{
|
||||
if (cache_) {
|
||||
return *cache_;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex_);
|
||||
|
||||
if (cache_ == nullptr) {
|
||||
cache_ = new T(std::forward<Args>(constructor_args)...);
|
||||
}
|
||||
return *cache_;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (!cache_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex_);
|
||||
|
||||
if (cache_) {
|
||||
delete cache_;
|
||||
cache_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock_guard()
|
||||
{
|
||||
return std::lock_guard(mutex_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
211
blender-5.2.0/source/blender/gpu/GPU_shader_builtin.hh
Normal file
211
blender-5.2.0/source/blender/gpu/GPU_shader_builtin.hh
Normal file
@@ -0,0 +1,211 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Set of shaders used for interface drawing.
|
||||
*
|
||||
* 2D shaders are not expected to work in 3D.
|
||||
* 3D shaders can work with 2D geometry and matrices.
|
||||
*
|
||||
* `INST` suffix means instance, which means the shader is build to leverage instancing
|
||||
* capabilities to reduce the number of draw-calls.
|
||||
*
|
||||
* For full list of parameters, search for the associated #ShaderCreateInfo.
|
||||
* Example: `GPU_SHADER_ICON` is defined by `GPU_SHADER_CREATE_INFO(gpu_shader_icon)`
|
||||
* Some parameters are builtins and are set automatically (ex: `ModelViewProjectionMatrix`).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
class Shader;
|
||||
} // namespace gpu
|
||||
|
||||
enum GPUBuiltinShader {
|
||||
/** Glyph drawing shader used by the BLF module. */
|
||||
GPU_SHADER_TEXT = 0,
|
||||
/** Draws keyframe markers. All markers shapes are supported through a single shader. */
|
||||
GPU_SHADER_KEYFRAME_SHAPE,
|
||||
/** Draw solid mesh with a single distant light using a clamped simple dot product. */
|
||||
GPU_SHADER_SIMPLE_LIGHTING,
|
||||
/** Draw an icon, leaving a semi-transparent rectangle on top of the icon. */
|
||||
GPU_SHADER_ICON,
|
||||
/** Draw a texture with a uniform color multiplied. */
|
||||
GPU_SHADER_2D_IMAGE_RECT_COLOR,
|
||||
/** Draw a texture with a desaturation factor. */
|
||||
GPU_SHADER_2D_IMAGE_DESATURATE_COLOR,
|
||||
/** Draw a group of texture rectangle with an associated color multiplied. */
|
||||
GPU_SHADER_ICON_MULTI,
|
||||
/** Draw a two color checker based on screen position (not UV coordinates). */
|
||||
GPU_SHADER_2D_CHECKER,
|
||||
/** Draw diagonal stripes with two alternating colors. */
|
||||
GPU_SHADER_2D_DIAG_STRIPES,
|
||||
/** Draw dashed lines with custom dash length and uniform color. */
|
||||
GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR,
|
||||
/** Draw triangles / lines / points with only depth output. */
|
||||
GPU_SHADER_3D_DEPTH_ONLY,
|
||||
/** Merge viewport overlay texture with the render output. */
|
||||
GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE,
|
||||
GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE,
|
||||
/** Merge viewport overlay texture with the render output. */
|
||||
GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR,
|
||||
/** Used for drawing of annotations (former grease pencil). */
|
||||
GPU_SHADER_GPENCIL_STROKE,
|
||||
/** Draw rounded area borders with silky smooth anti-aliasing without any over-draw. */
|
||||
GPU_SHADER_2D_AREA_BORDERS,
|
||||
/** Multi usage widget shaders for drawing buttons and other UI elements. */
|
||||
GPU_SHADER_2D_WIDGET_BASE,
|
||||
GPU_SHADER_2D_WIDGET_BASE_INST,
|
||||
GPU_SHADER_2D_WIDGET_SHADOW,
|
||||
/** Draw a node socket given it's bounding rectangle. All socket shapes are supported through
|
||||
* a single shader. */
|
||||
GPU_SHADER_2D_NODE_SOCKET,
|
||||
GPU_SHADER_2D_NODE_SOCKET_INST,
|
||||
/** Draw a node link given an input quadratic Bezier curve. */
|
||||
GPU_SHADER_2D_NODELINK,
|
||||
|
||||
/** Draw round points with per vertex size and color. */
|
||||
GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR,
|
||||
/** Draw round points with a uniform size. Disabling blending will disable AA. */
|
||||
GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA,
|
||||
GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA,
|
||||
/** Draw round points with a uniform size and an outline. Disabling blending will disable AA. */
|
||||
GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA,
|
||||
|
||||
/** Draw geometry with uniform color. Has an additional clip plane parameter. */
|
||||
GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR,
|
||||
/** Draw wide lines with uniform color. Has an additional clip plane parameter. */
|
||||
GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR,
|
||||
|
||||
/** Draw strip widgets in sequencer timeline. */
|
||||
GPU_SHADER_SEQUENCER_STRIPS,
|
||||
/** Draw strip thumbnails in sequencer timeline. */
|
||||
GPU_SHADER_SEQUENCER_THUMBS,
|
||||
/** Rasterize sequencer scope points into buffers via compute. */
|
||||
GPU_SHADER_SEQUENCER_SCOPE_RASTER,
|
||||
/** Resolve rasterized scope point buffers to display. */
|
||||
GPU_SHADER_SEQUENCER_SCOPE_RESOLVE,
|
||||
/** Draw sequencer zebra pattern (overexposed regions). */
|
||||
GPU_SHADER_SEQUENCER_ZEBRA,
|
||||
|
||||
/** Compute shaders to generate 2d index buffers (mainly for curve drawing). */
|
||||
GPU_SHADER_INDEXBUF_POINTS,
|
||||
GPU_SHADER_INDEXBUF_LINES,
|
||||
GPU_SHADER_INDEXBUF_TRIS,
|
||||
|
||||
/** Compute shaders to generate mipmaps. */
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8_LAYERED,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8_8_8_8,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8_8_8_8_LAYERED,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16_LAYERED,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16_16_16_16,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16_16_16_16_LAYERED,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32_LAYERED,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32_32_32_32,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32_32_32_32_LAYERED,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SRGBA_8_8_8_8,
|
||||
GPU_SHADER_2D_UPDATE_MIPMAPS_SRGBA_8_8_8_8_LAYERED,
|
||||
|
||||
/**
|
||||
* ----------------------- Shaders exposed through pyGPU module -----------------------
|
||||
*
|
||||
* Avoid breaking the interface of these shaders as they are used by addons.
|
||||
* Polyline versions are used for drawing wide lines (> 1px width).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Take a 3D position and color for each vertex without color interpolation.
|
||||
*
|
||||
* \param color: in vec4
|
||||
* \param pos: in vec3
|
||||
*/
|
||||
GPU_SHADER_3D_FLAT_COLOR,
|
||||
GPU_SHADER_3D_POLYLINE_FLAT_COLOR,
|
||||
GPU_SHADER_3D_POINT_FLAT_COLOR,
|
||||
|
||||
/**
|
||||
* Take a 3D position and color for each vertex with perspective correct interpolation.
|
||||
*
|
||||
* \param color: in vec4
|
||||
* \param pos: in vec3
|
||||
*/
|
||||
GPU_SHADER_3D_SMOOTH_COLOR,
|
||||
GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR,
|
||||
|
||||
/**
|
||||
* Take a single color for all the vertices and a 3D position for each vertex.
|
||||
*
|
||||
* \param color: uniform vec4
|
||||
* \param pos: in vec3
|
||||
*/
|
||||
GPU_SHADER_3D_UNIFORM_COLOR,
|
||||
GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR,
|
||||
GPU_SHADER_3D_POINT_UNIFORM_COLOR,
|
||||
|
||||
/**
|
||||
* Draw a sRGB color space texture in 3D.
|
||||
* Texture color space is assumed to match the frame-buffer.
|
||||
* Take a 3D position and a 2D texture coordinate for each vertex.
|
||||
*
|
||||
* \param image: uniform sampler2D
|
||||
* \param texCoord: in vec2
|
||||
* \param pos: in vec3
|
||||
*/
|
||||
GPU_SHADER_3D_IMAGE,
|
||||
/**
|
||||
* Draw a scene linear color space texture in 3D.
|
||||
* Texture value is transformed to the Rec.709 sRGB color space.
|
||||
* Take a 3D position and a 2D texture coordinate for each vertex.
|
||||
*
|
||||
* \param image: uniform sampler2D
|
||||
* \param texCoord: in vec2
|
||||
* \param pos: in vec3
|
||||
*/
|
||||
GPU_SHADER_3D_IMAGE_SCENE_LINEAR_TO_REC709_SRGB,
|
||||
/**
|
||||
* Draw a sRGB color space (with Rec.709 primaries) texture in 3D.
|
||||
* Take a 3D position and color for each vertex with linear interpolation in window space.
|
||||
*
|
||||
* \param color: uniform vec4
|
||||
* \param image: uniform sampler2D
|
||||
* \param texCoord: in vec2
|
||||
* \param pos: in vec3
|
||||
*/
|
||||
GPU_SHADER_3D_IMAGE_COLOR,
|
||||
/**
|
||||
* Draw a scene linear color space texture in 3D.
|
||||
* Texture value is transformed to the Rec.709 sRGB color space.
|
||||
* Take a 3D position and color for each vertex with linear interpolation in window space.
|
||||
*
|
||||
* \param color: uniform vec4
|
||||
* \param image: uniform sampler2D
|
||||
* \param texCoord: in vec2
|
||||
* \param pos: in vec3
|
||||
*/
|
||||
GPU_SHADER_3D_IMAGE_COLOR_SCENE_LINEAR_TO_REC709_SRGB,
|
||||
};
|
||||
#define GPU_SHADER_BUILTIN_LEN (GPU_SHADER_3D_IMAGE_COLOR_SCENE_LINEAR_TO_REC709_SRGB + 1)
|
||||
|
||||
/** Support multiple configurations. */
|
||||
enum GPUShaderConfig {
|
||||
GPU_SHADER_CFG_DEFAULT = 0,
|
||||
GPU_SHADER_CFG_CLIPPED = 1,
|
||||
};
|
||||
#define GPU_SHADER_CFG_LEN (GPU_SHADER_CFG_CLIPPED + 1)
|
||||
|
||||
gpu::Shader *GPU_shader_get_builtin_shader_with_config(GPUBuiltinShader shader,
|
||||
GPUShaderConfig sh_cfg);
|
||||
gpu::Shader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader);
|
||||
|
||||
void GPU_shader_builtin_warm_up();
|
||||
|
||||
void GPU_shader_free_builtin_shaders();
|
||||
|
||||
} // namespace blender
|
||||
267
blender-5.2.0/source/blender/gpu/GPU_shader_shared.hh
Normal file
267
blender-5.2.0/source/blender/gpu/GPU_shader_shared.hh
Normal file
@@ -0,0 +1,267 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader_shared_utils.hh"
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
struct TestOutputRawData;
|
||||
#endif
|
||||
|
||||
/* NOTE: float3 has differing stride and alignment rules across different GPU back-ends. If 12 byte
|
||||
* stride and alignment is essential, use `packed_float3` to avoid data read issues. This is
|
||||
* required in the common use-case where a float3 and an int/float are paired together for optimal
|
||||
* data transfer. */
|
||||
|
||||
enum [[host_shared]] GPUKeyframeShapes : uint32_t {
|
||||
GPU_KEYFRAME_SHAPE_DIAMOND = (1u << 0u),
|
||||
GPU_KEYFRAME_SHAPE_CIRCLE = (1u << 1u),
|
||||
GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL = (1u << 2u),
|
||||
GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL = (1u << 3u),
|
||||
GPU_KEYFRAME_SHAPE_INNER_DOT = (1u << 4u),
|
||||
GPU_KEYFRAME_SHAPE_ARROW_END_MAX = (1u << 8u),
|
||||
GPU_KEYFRAME_SHAPE_ARROW_END_MIN = (1u << 9u),
|
||||
GPU_KEYFRAME_SHAPE_ARROW_END_MIXED = (1u << 10u),
|
||||
GPU_KEYFRAME_SHAPE_SQUARE = (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL |
|
||||
GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL),
|
||||
};
|
||||
|
||||
#define MAX_SOCKET_PARAMETERS 4
|
||||
#define MAX_SOCKET_INSTANCE 32
|
||||
|
||||
/* Node Socket shader parameters. Must match the shader layout of "gpu_shader_2D_node_socket". */
|
||||
struct [[host_shared]] NodeSocketShaderParameters {
|
||||
float4 rect;
|
||||
float4 color_inner;
|
||||
float4 color_outline;
|
||||
float outline_thickness;
|
||||
float outline_offset;
|
||||
float shape;
|
||||
float aspect;
|
||||
};
|
||||
|
||||
/* Per link data. */
|
||||
struct [[host_shared]] NodeLinkData {
|
||||
float4 start_color;
|
||||
float4 end_color;
|
||||
float2 bezier_P0;
|
||||
float2 bezier_P1;
|
||||
float2 bezier_P2;
|
||||
float2 bezier_P3;
|
||||
uint color_ids;
|
||||
float dash_length;
|
||||
float dash_factor;
|
||||
float dash_alpha;
|
||||
float dim_factor;
|
||||
float thickness;
|
||||
float aspect;
|
||||
bool32_t do_arrow;
|
||||
bool32_t do_muted;
|
||||
bool32_t has_back_link;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
};
|
||||
|
||||
/* Data common to all links. */
|
||||
struct [[host_shared]] NodeLinkUniformData {
|
||||
float4 colors[6];
|
||||
float aspect;
|
||||
float arrow_size;
|
||||
float2 _pad;
|
||||
};
|
||||
|
||||
struct [[host_shared]] GPencilStrokeData {
|
||||
float2 viewport;
|
||||
float pixsize;
|
||||
float objscale;
|
||||
int caps_start;
|
||||
int caps_end;
|
||||
bool32_t fill_stroke;
|
||||
float _pad;
|
||||
};
|
||||
|
||||
struct [[host_shared]] GPUClipPlanes {
|
||||
float4x4 ClipModelMatrix;
|
||||
float4 world[6];
|
||||
};
|
||||
|
||||
struct [[host_shared]] SimpleLightingData {
|
||||
float4 l_color;
|
||||
packed_float3 light;
|
||||
float _pad;
|
||||
};
|
||||
|
||||
#define MAX_CALLS 16
|
||||
|
||||
struct [[host_shared]] MultiIconCallData {
|
||||
float4 calls_data[MAX_CALLS * 3];
|
||||
};
|
||||
|
||||
#define GPU_SEQ_STRIP_DRAW_DATA_LEN 256
|
||||
|
||||
enum [[host_shared]] GPUSeqFlags : uint32_t {
|
||||
GPU_SEQ_FLAG_BACKGROUND = (1u << 0u),
|
||||
GPU_SEQ_FLAG_SINGLE_IMAGE = (1u << 1u),
|
||||
GPU_SEQ_FLAG_COLOR_BAND = (1u << 2u),
|
||||
GPU_SEQ_FLAG_TRANSITION = (1u << 3u),
|
||||
GPU_SEQ_FLAG_LOCKED = (1u << 4u),
|
||||
GPU_SEQ_FLAG_MISSING_TITLE = (1u << 5u),
|
||||
GPU_SEQ_FLAG_MISSING_CONTENT = (1u << 6u),
|
||||
GPU_SEQ_FLAG_SELECTED = (1u << 7u),
|
||||
GPU_SEQ_FLAG_ACTIVE = (1u << 8u),
|
||||
GPU_SEQ_FLAG_HIGHLIGHT = (1u << 9u),
|
||||
GPU_SEQ_FLAG_BORDER = (1u << 10u),
|
||||
GPU_SEQ_FLAG_SELECTED_LH = (1u << 11u),
|
||||
GPU_SEQ_FLAG_SELECTED_RH = (1u << 12u),
|
||||
GPU_SEQ_FLAG_OVERLAP = (1u << 15u),
|
||||
GPU_SEQ_FLAG_CLAMPED = (1u << 16u),
|
||||
|
||||
GPU_SEQ_FLAG_ANY_HANDLE = GPU_SEQ_FLAG_SELECTED_LH | GPU_SEQ_FLAG_SELECTED_RH
|
||||
};
|
||||
|
||||
/* Glyph for text rendering. */
|
||||
struct [[host_shared]] GlyphQuad {
|
||||
int4 position;
|
||||
float4 glyph_color; /* Cannot be name `color` because of metal macros. */
|
||||
int2 glyph_size;
|
||||
int offset;
|
||||
uint flags;
|
||||
};
|
||||
|
||||
/* VSE per-strip data for timeline rendering. */
|
||||
struct [[host_shared]] SeqStripDrawData {
|
||||
/* Horizontal strip positions (1.0 is one frame). */
|
||||
/* Left and right strip sides. */
|
||||
float left_handle;
|
||||
float right_handle;
|
||||
/* Start and end of actual content (only relevant for strips that have holdout regions). */
|
||||
float content_start;
|
||||
float content_end;
|
||||
float handle_width;
|
||||
/* Vertical strip positions (1.0 is one channel). */
|
||||
float bottom;
|
||||
float top;
|
||||
float strip_content_top; /* Content coordinate, i.e. below title bar if there is one. */
|
||||
uint flags; /* GPUSeqFlags bitmask. */
|
||||
/* Strip colors, each is uchar4 packed with equivalent of packUnorm4x8. */
|
||||
uint col_background;
|
||||
uint col_outline;
|
||||
uint col_color_band;
|
||||
uint col_transition_in;
|
||||
uint col_transition_out;
|
||||
float _pad0;
|
||||
float _pad1;
|
||||
};
|
||||
/* clang-format off */ /* Keep one line. Avoid issues with shader error line. */
|
||||
BLI_STATIC_ASSERT(sizeof(SeqStripDrawData) * GPU_SEQ_STRIP_DRAW_DATA_LEN <= 16384, "SeqStripDrawData UBO must not exceed minspec UBO size (16384)")
|
||||
/* clang-format on */
|
||||
|
||||
/* VSE per-thumbnail data for timeline rendering. */
|
||||
struct [[host_shared]] SeqStripThumbData {
|
||||
/* Strip rectangle positions. */
|
||||
float left;
|
||||
float right;
|
||||
float bottom;
|
||||
float top;
|
||||
/* Thumbnail rectangle positions. */
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
/* Thumbnail UVs. */
|
||||
float u1;
|
||||
float v1;
|
||||
float u2;
|
||||
float v2;
|
||||
float4 tint_color;
|
||||
};
|
||||
/* clang-format off */ /* Keep one line. Avoid issues with shader error line. */
|
||||
BLI_STATIC_ASSERT(sizeof(SeqStripThumbData) * GPU_SEQ_STRIP_DRAW_DATA_LEN <= 16384, "SeqStripThumbData UBO must not exceed minspec UBO size (16384)")
|
||||
/* clang-format on */
|
||||
|
||||
/* VSE global data for timeline rendering. */
|
||||
struct [[host_shared]] SeqContextDrawData {
|
||||
float round_radius;
|
||||
float pixelsize;
|
||||
uint col_back;
|
||||
float _pad0;
|
||||
};
|
||||
|
||||
/* VSE scope point rasterizer data. */
|
||||
struct [[host_shared]] SeqScopeRasterData {
|
||||
uint col_r;
|
||||
uint col_g;
|
||||
uint col_b;
|
||||
uint col_a;
|
||||
};
|
||||
|
||||
struct [[host_shared]] GreasePencilStrokeData {
|
||||
packed_float3 position;
|
||||
float stroke_thickness;
|
||||
float4 stroke_color;
|
||||
};
|
||||
|
||||
enum [[host_shared]] TestStatus : uint32_t {
|
||||
TEST_STATUS_NONE = 0u,
|
||||
TEST_STATUS_PASSED = 1u,
|
||||
TEST_STATUS_FAILED = 2u,
|
||||
};
|
||||
enum [[host_shared]] TestType : uint32_t {
|
||||
TEST_TYPE_BOOL = 0u,
|
||||
TEST_TYPE_UINT = 1u,
|
||||
TEST_TYPE_INT = 2u,
|
||||
TEST_TYPE_FLOAT = 3u,
|
||||
TEST_TYPE_IVEC2 = 4u,
|
||||
TEST_TYPE_IVEC3 = 5u,
|
||||
TEST_TYPE_IVEC4 = 6u,
|
||||
TEST_TYPE_UVEC2 = 7u,
|
||||
TEST_TYPE_UVEC3 = 8u,
|
||||
TEST_TYPE_UVEC4 = 9u,
|
||||
TEST_TYPE_VEC2 = 10u,
|
||||
TEST_TYPE_VEC3 = 11u,
|
||||
TEST_TYPE_VEC4 = 12u,
|
||||
TEST_TYPE_MAT2X2 = 13u,
|
||||
TEST_TYPE_MAT2X3 = 14u,
|
||||
TEST_TYPE_MAT2X4 = 15u,
|
||||
TEST_TYPE_MAT3X2 = 16u,
|
||||
TEST_TYPE_MAT3X3 = 17u,
|
||||
TEST_TYPE_MAT3X4 = 18u,
|
||||
TEST_TYPE_MAT4X2 = 19u,
|
||||
TEST_TYPE_MAT4X3 = 20u,
|
||||
TEST_TYPE_MAT4X4 = 21u,
|
||||
};
|
||||
|
||||
/** \note Contains arrays of scalar. To be use only with SSBOs to avoid padding issues. */
|
||||
struct [[host_shared]] TestOutputRawData {
|
||||
uint data[16];
|
||||
};
|
||||
|
||||
struct [[host_shared]] TestOutput {
|
||||
struct TestOutputRawData expect;
|
||||
struct TestOutputRawData result;
|
||||
/** TestStatus. */
|
||||
uint status;
|
||||
/** Line error in the GLSL file. */
|
||||
int line;
|
||||
/** TestType of expect and result. */
|
||||
uint type;
|
||||
int _pad0;
|
||||
};
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
TestOutput test_output(TestOutputRawData expect, TestOutputRawData result, bool status, uint type)
|
||||
{
|
||||
TestOutput test;
|
||||
test.expect = expect;
|
||||
test.result = result;
|
||||
test.status = status ? TEST_STATUS_PASSED : TEST_STATUS_FAILED;
|
||||
test.type = type;
|
||||
return test;
|
||||
}
|
||||
#endif
|
||||
145
blender-5.2.0/source/blender/gpu/GPU_shader_shared_utils.hh
Normal file
145
blender-5.2.0/source/blender/gpu/GPU_shader_shared_utils.hh
Normal file
@@ -0,0 +1,145 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Glue definition to make shared declaration of struct & functions work in both C / C++ and GLSL.
|
||||
* We use the same vector and matrix types as Blender C++. Some math functions are defined to use
|
||||
* the float version to match the GLSL syntax.
|
||||
* This file can be used for C & C++ code and the syntax used should follow the same rules.
|
||||
* Some preprocessing is done by the GPU back-end to make it GLSL compatible.
|
||||
*
|
||||
* IMPORTANT:
|
||||
* - Always use `u` suffix for enum values. GLSL do not support implicit cast.
|
||||
* - Define all values. This is in order to simplify custom pre-processor code.
|
||||
* - (C++ only) Always use `uint32_t` as underlying type (`enum MyEnum : uint32_t`).
|
||||
* - (C only) do NOT use the enum type inside UBO/SSBO structs and use `uint` instead.
|
||||
* - Use float suffix by default for float literals to avoid double promotion in C++.
|
||||
* - Pack one float or int after a vec3/ivec3 to fulfill alignment rules.
|
||||
*
|
||||
* NOTE: Due to alignment restriction and buggy drivers, do not try to use mat3 inside structs.
|
||||
* NOTE: (UBO only) Do not use arrays of float. They are padded to arrays of vec4 and are not worth
|
||||
* it. This does not apply to SSBO.
|
||||
*
|
||||
* IMPORTANT: Do not forget to align mat4, vec3 and vec4 to 16 bytes, and vec2 to 8 bytes.
|
||||
*
|
||||
* NOTE: You can use bool type using bool32_t a int boolean type matching the GLSL type.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
/* Silence macros when compiling for shaders. */
|
||||
# define BLI_STATIC_ASSERT(cond, msg)
|
||||
# define BLI_STATIC_ASSERT_ALIGN(type_, align_)
|
||||
# define BLI_STATIC_ASSERT_SIZE(type_, size_)
|
||||
# define ENUM_OPERATORS(a)
|
||||
# define UNUSED_VARS(a) (void)a
|
||||
/* Math function renaming. */
|
||||
# define cosf cos
|
||||
# define sinf sin
|
||||
# define tanf tan
|
||||
# define acosf acos
|
||||
# define asinf asin
|
||||
# define atanf atan
|
||||
# define floorf floor
|
||||
# define ceilf ceil
|
||||
# define sqrtf sqrt
|
||||
# define expf exp
|
||||
|
||||
#elif defined(GPU_SHADER)
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
/* Silence macros when compiling for shaders. */
|
||||
# define BLI_STATIC_ASSERT(cond, msg)
|
||||
# define BLI_STATIC_ASSERT_ALIGN(type_, align_)
|
||||
# define BLI_STATIC_ASSERT_SIZE(type_, size_)
|
||||
# define ENUM_OPERATORS(a)
|
||||
# define UNUSED_VARS(a)
|
||||
/* Math function renaming. */
|
||||
# define cosf cos
|
||||
# define sinf sin
|
||||
# define tanf tan
|
||||
# define acosf acos
|
||||
# define asinf asin
|
||||
# define atanf atan
|
||||
# define floorf floor
|
||||
# define ceilf ceil
|
||||
# define sqrtf sqrt
|
||||
# define expf exp
|
||||
|
||||
#else /* C / C++ */
|
||||
# ifndef GPU_SHADER /* Avoid parsing this into shader code. */
|
||||
|
||||
# include "BLI_assert.h"
|
||||
# include "BLI_enum_flags.hh"
|
||||
# include "BLI_sys_types.h"
|
||||
|
||||
# include <math.h>
|
||||
|
||||
# include "BLI_math_matrix_types.hh"
|
||||
# include "BLI_math_vector_types.hh"
|
||||
|
||||
using bool32_t = int32_t;
|
||||
// using bool2 = blender::int2; /* Size is not consistent across backend. */
|
||||
// using bool3 = blender::int3; /* Size is not consistent across backend. */
|
||||
// using bool4 = blender::int4; /* Size is not consistent across backend. */
|
||||
|
||||
using blender::float2;
|
||||
using blender::float4;
|
||||
using blender::int2;
|
||||
using blender::int4;
|
||||
using blender::uint2;
|
||||
using blender::uint4;
|
||||
/** IMPORTANT: Do not use in shared struct. Use packed_(float/int/uint)3 instead.
|
||||
* Here for static functions usage only. */
|
||||
using blender::float3;
|
||||
using blender::int3;
|
||||
using blender::uint3;
|
||||
/** Packed types are needed for MSL which have different alignment rules for float3. */
|
||||
using packed_float3 = blender::float3;
|
||||
using packed_int3 = blender::int3;
|
||||
using packed_uint3 = blender::uint3;
|
||||
|
||||
using blender::float2x2;
|
||||
// using blender::float3x2; /* Does not follow alignment rules of GPU. */
|
||||
using blender::float4x2;
|
||||
// using blender::float2x3; /* Does not follow alignment rules of GPU. */
|
||||
// using blender::float3x3; /* Does not follow alignment rules of GPU. */
|
||||
// using blender::float4x3; /* Does not follow alignment rules of GPU. */
|
||||
using blender::float2x4;
|
||||
using blender::float3x4;
|
||||
using blender::float4x4;
|
||||
|
||||
/**
|
||||
* Wrapper type for members of unions in host shared structure.
|
||||
* Passthrough implementation when compiled with host code.
|
||||
* See gpu_shader_compat_cxx.hh for the C++ stub implementation.
|
||||
* This implementation needs to have the correct base type and can safely cast to it.
|
||||
*/
|
||||
template<typename T> struct union_t {
|
||||
T data;
|
||||
|
||||
const T &operator()() const
|
||||
{
|
||||
return *reinterpret_cast<const T *>(this);
|
||||
}
|
||||
|
||||
T &operator()()
|
||||
{
|
||||
return *reinterpret_cast<T *>(this);
|
||||
}
|
||||
};
|
||||
|
||||
/* Mute the following attributes. Avoid warning about unknown attribute.
|
||||
* These are parsed by our shader translation tool. */
|
||||
|
||||
/* To be used on struct. Means the layout is to be used with uniform or storage buffers. */
|
||||
# define host_shared
|
||||
|
||||
# endif
|
||||
#endif
|
||||
243
blender-5.2.0/source/blender/gpu/GPU_state.hh
Normal file
243
blender-5.2.0/source/blender/gpu/GPU_state.hh
Normal file
@@ -0,0 +1,243 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** Opaque type hiding gpu::Fence. */
|
||||
struct GPUFence;
|
||||
|
||||
enum GPUWriteMask {
|
||||
GPU_WRITE_NONE = 0,
|
||||
GPU_WRITE_RED = (1 << 0),
|
||||
GPU_WRITE_GREEN = (1 << 1),
|
||||
GPU_WRITE_BLUE = (1 << 2),
|
||||
GPU_WRITE_ALPHA = (1 << 3),
|
||||
GPU_WRITE_DEPTH = (1 << 4),
|
||||
GPU_WRITE_STENCIL = (1 << 5),
|
||||
GPU_WRITE_COLOR = (GPU_WRITE_RED | GPU_WRITE_GREEN | GPU_WRITE_BLUE | GPU_WRITE_ALPHA),
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUWriteMask)
|
||||
|
||||
enum GPUBarrier {
|
||||
/* Texture Barrier. */
|
||||
|
||||
/** All written texture prior to this barrier can be bound as frame-buffer attachment. */
|
||||
GPU_BARRIER_FRAMEBUFFER = (1 << 0),
|
||||
/** All written texture prior to this barrier can be bound as image. */
|
||||
GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 1),
|
||||
/** All written texture prior to this barrier can be bound as sampler. */
|
||||
GPU_BARRIER_TEXTURE_FETCH = (1 << 2),
|
||||
/** All written texture prior to this barrier can be read or updated with CPU memory. */
|
||||
GPU_BARRIER_TEXTURE_UPDATE = (1 << 3),
|
||||
/** All written texture prior to this barrier can be read or updated with PBO. */
|
||||
// GPU_BARRIER_PIXEL_BUFFER = (1 << 4), /* Not implemented yet. */
|
||||
|
||||
/* Buffer Barrier. */
|
||||
|
||||
/** All written buffer prior to this barrier can be bound as indirect command buffer. */
|
||||
GPU_BARRIER_COMMAND = (1 << 10),
|
||||
/** All written buffer prior to this barrier can be bound as SSBO. */
|
||||
GPU_BARRIER_SHADER_STORAGE = (1 << 11),
|
||||
/** All written buffer prior to this barrier can be bound as VBO. */
|
||||
GPU_BARRIER_VERTEX_ATTRIB_ARRAY = (1 << 12),
|
||||
/** All written buffer prior to this barrier can be bound as IBO. */
|
||||
GPU_BARRIER_ELEMENT_ARRAY = (1 << 13),
|
||||
/** All written buffer prior to this barrier can be bound as UBO. */
|
||||
GPU_BARRIER_UNIFORM = (1 << 14),
|
||||
/** All written buffer prior to this barrier can be read or updated with CPU memory. */
|
||||
GPU_BARRIER_BUFFER_UPDATE = (1 << 15),
|
||||
/** All written persistent mapped buffer prior to this barrier can be read or updated. */
|
||||
// GPU_BARRIER_CLIENT_MAPPED_BUFFER = (1 << 15), /* Not implemented yet. */
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUBarrier)
|
||||
|
||||
/* NOTE: For Metal and Vulkan only.
|
||||
* TODO(Metal): Update barrier calls to use stage flags. */
|
||||
enum GPUStageBarrierBits {
|
||||
GPU_BARRIER_STAGE_VERTEX = (1 << 0),
|
||||
GPU_BARRIER_STAGE_FRAGMENT = (1 << 1),
|
||||
GPU_BARRIER_STAGE_COMPUTE = (1 << 2),
|
||||
GPU_BARRIER_STAGE_ANY_GRAPHICS = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT),
|
||||
GPU_BARRIER_STAGE_ANY = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT |
|
||||
GPU_BARRIER_STAGE_COMPUTE),
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUStageBarrierBits)
|
||||
|
||||
/**
|
||||
* Defines the fixed pipeline blending equation.
|
||||
* SRC is the output color from the shader.
|
||||
* DST is the color from the frame-buffer.
|
||||
* The blending equation is:
|
||||
* `(SRC * A) + (DST * B)`.
|
||||
* The blend mode will modify the A and B parameters.
|
||||
*/
|
||||
enum GPUBlend {
|
||||
GPU_BLEND_NONE = 0,
|
||||
/** Pre-multiply variants will _NOT_ multiply rgb output by alpha. */
|
||||
GPU_BLEND_ALPHA,
|
||||
GPU_BLEND_ALPHA_PREMULT,
|
||||
GPU_BLEND_ADDITIVE,
|
||||
GPU_BLEND_ADDITIVE_PREMULT,
|
||||
GPU_BLEND_MULTIPLY,
|
||||
GPU_BLEND_SUBTRACT,
|
||||
/** Replace logic op: SRC * (1 - DST)
|
||||
* NOTE: Does not modify alpha. */
|
||||
GPU_BLEND_INVERT,
|
||||
/** Stores min(SRC, DST) per component. */
|
||||
GPU_BLEND_MIN,
|
||||
/** Stores max(SRC, DST) per component. */
|
||||
GPU_BLEND_MAX,
|
||||
/** Order independent transparency.
|
||||
* NOTE: Cannot be used as is. Needs special setup (frame-buffer, shader ...). */
|
||||
GPU_BLEND_OIT,
|
||||
/** Special blend to add color under and multiply DST color by SRC alpha. */
|
||||
GPU_BLEND_BACKGROUND,
|
||||
/** Custom blend parameters using dual source blending : SRC0 + SRC1 * DST
|
||||
* NOTE: Can only be used with _ONE_ Draw Buffer and shader needs to be specialized. */
|
||||
GPU_BLEND_CUSTOM,
|
||||
GPU_BLEND_ALPHA_UNDER_PREMUL,
|
||||
/** Multiplies every channel (alpha included) by `1 - SRC.a`. Used for piercing a hole using an
|
||||
* image alpha channel. */
|
||||
GPU_BLEND_OVERLAY_MASK_FROM_ALPHA,
|
||||
/**
|
||||
* Alpha channel is interpreted as transmittance (aka transparency) and not alpha.
|
||||
* To be used with a frame-buffer with alpha cleared to 1 for full transparency.
|
||||
* Equivalent to: `DST.rgba * SRC.a + float4(SRC.rgb, 0.0)`.
|
||||
*/
|
||||
GPU_BLEND_TRANSPARENCY,
|
||||
};
|
||||
|
||||
enum GPUDepthTest {
|
||||
GPU_DEPTH_NONE = 0,
|
||||
GPU_DEPTH_ALWAYS, /* Used to draw to the depth buffer without really testing. */
|
||||
GPU_DEPTH_LESS,
|
||||
GPU_DEPTH_LESS_EQUAL, /* Default. */
|
||||
GPU_DEPTH_EQUAL,
|
||||
GPU_DEPTH_GREATER,
|
||||
GPU_DEPTH_GREATER_EQUAL,
|
||||
};
|
||||
|
||||
enum GPUStencilTest {
|
||||
GPU_STENCIL_NONE = 0,
|
||||
GPU_STENCIL_ALWAYS,
|
||||
GPU_STENCIL_EQUAL,
|
||||
GPU_STENCIL_NEQUAL,
|
||||
};
|
||||
|
||||
enum GPUStencilOp {
|
||||
GPU_STENCIL_OP_NONE = 0,
|
||||
GPU_STENCIL_OP_REPLACE,
|
||||
/** Special values for stencil shadows. */
|
||||
GPU_STENCIL_OP_COUNT_DEPTH_PASS,
|
||||
GPU_STENCIL_OP_COUNT_DEPTH_FAIL,
|
||||
};
|
||||
|
||||
enum GPUFaceCullTest {
|
||||
GPU_CULL_NONE = 0, /* Culling disabled. */
|
||||
GPU_CULL_FRONT,
|
||||
GPU_CULL_BACK,
|
||||
};
|
||||
|
||||
enum GPUProvokingVertex {
|
||||
GPU_VERTEX_LAST = 0, /* Default. */
|
||||
GPU_VERTEX_FIRST = 1, /* Follow Blender loop order. */
|
||||
};
|
||||
|
||||
void GPU_blend(GPUBlend blend);
|
||||
void GPU_face_culling(GPUFaceCullTest culling);
|
||||
void GPU_depth_test(GPUDepthTest test);
|
||||
void GPU_stencil_test(GPUStencilTest test);
|
||||
void GPU_provoking_vertex(GPUProvokingVertex vert);
|
||||
void GPU_front_facing(bool invert);
|
||||
void GPU_scissor_test(bool enable);
|
||||
void GPU_line_smooth(bool enable);
|
||||
/**
|
||||
* \note By convention, this is set as needed and not reset back to 1.0.
|
||||
* This means code that draws lines must always set the line width beforehand,
|
||||
* but is not expected to restore its previous value.
|
||||
*/
|
||||
void GPU_line_width(float width);
|
||||
void GPU_logic_op_xor_set(bool enable);
|
||||
void GPU_point_size(float size);
|
||||
void GPU_polygon_smooth(bool enable);
|
||||
|
||||
/**
|
||||
* Programmable point size:
|
||||
* - Shaders set their own point size when enabled
|
||||
* - Use GPU_point_size when disabled.
|
||||
*
|
||||
* TODO: remove and use program point size everywhere.
|
||||
*/
|
||||
void GPU_program_point_size(bool enable);
|
||||
void GPU_scissor(int x, int y, int width, int height);
|
||||
void GPU_scissor_get(int coords[4]);
|
||||
void GPU_viewport(int x, int y, int width, int height);
|
||||
void GPU_viewport_size_get_f(float coords[4]);
|
||||
void GPU_viewport_size_get_i(int coords[4]);
|
||||
void GPU_write_mask(GPUWriteMask mask);
|
||||
void GPU_color_mask(bool r, bool g, bool b, bool a);
|
||||
void GPU_depth_mask(bool depth);
|
||||
bool GPU_depth_mask_get();
|
||||
void GPU_clip_distances(int distances_enabled);
|
||||
void GPU_state_set(GPUWriteMask write_mask,
|
||||
GPUBlend blend,
|
||||
GPUFaceCullTest culling_test,
|
||||
GPUDepthTest depth_test,
|
||||
GPUStencilTest stencil_test,
|
||||
GPUStencilOp stencil_op,
|
||||
GPUProvokingVertex provoking_vert);
|
||||
|
||||
void GPU_stencil_reference_set(uint reference);
|
||||
void GPU_stencil_write_mask_set(uint write_mask);
|
||||
void GPU_stencil_compare_mask_set(uint compare_mask);
|
||||
|
||||
/* Sets the depth range to be 0..1. Only have effect with the OpenGL backend. Have no effect if
|
||||
* glClipControl is not supported. Shaders used for drawing with this state must use
|
||||
* BuiltinBits::CLIP_CONTROL for their vertex shader to be patched. */
|
||||
void GPU_clip_control_unit_range(bool enable);
|
||||
|
||||
GPUFaceCullTest GPU_face_culling_get();
|
||||
GPUBlend GPU_blend_get();
|
||||
GPUDepthTest GPU_depth_test_get();
|
||||
GPUWriteMask GPU_write_mask_get();
|
||||
uint GPU_stencil_mask_get();
|
||||
GPUStencilTest GPU_stencil_test_get();
|
||||
/**
|
||||
* \note Already pre-multiplied by `U.pixelsize`.
|
||||
*/
|
||||
float GPU_line_width_get();
|
||||
bool GPU_line_smooth_get();
|
||||
|
||||
void GPU_flush();
|
||||
void GPU_finish();
|
||||
void GPU_apply_state();
|
||||
|
||||
/**
|
||||
* A barrier _must_ be issued _after_ a shader arbitrary write to a buffer or a
|
||||
* texture (i.e: using imageStore, imageAtomics, or SSBO). Otherwise, the written value may not
|
||||
* appear updated to the next user of this resource.
|
||||
*
|
||||
* The type of barrier must be chosen depending on the _future_ use of the memory that was written
|
||||
* by the shader.
|
||||
*/
|
||||
void GPU_memory_barrier(GPUBarrier barrier);
|
||||
|
||||
GPUFence *GPU_fence_create();
|
||||
void GPU_fence_free(GPUFence *fence);
|
||||
void GPU_fence_signal(GPUFence *fence);
|
||||
void GPU_fence_wait(GPUFence *fence);
|
||||
|
||||
} // namespace blender
|
||||
98
blender-5.2.0/source/blender/gpu/GPU_storage_buffer.hh
Normal file
98
blender-5.2.0/source/blender/gpu/GPU_storage_buffer.hh
Normal file
@@ -0,0 +1,98 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Storage buffers API. Used to handle many way bigger buffers than Uniform buffers update at once.
|
||||
* Make sure that the data structure is compatible with what the implementation expect.
|
||||
* (see "7.8 Shader Buffer Variables and Shader Storage Blocks" from the OpenGL spec for more info
|
||||
* about std430 layout)
|
||||
* Rule of thumb: Padding to 16bytes, don't use vec3.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class StorageBuf;
|
||||
} // namespace gpu
|
||||
|
||||
gpu::StorageBuf *GPU_storagebuf_create_ex(size_t size,
|
||||
const void *data,
|
||||
GPUUsageType usage,
|
||||
const char *name);
|
||||
|
||||
#define GPU_storagebuf_create(size) \
|
||||
GPU_storagebuf_create_ex(size, nullptr, GPU_USAGE_DYNAMIC, __func__);
|
||||
|
||||
void GPU_storagebuf_free(gpu::StorageBuf *ssbo);
|
||||
|
||||
/**
|
||||
* Limit the size of the storage buffer.
|
||||
*
|
||||
* Backends can optimize data transfers using the size that is actually used.
|
||||
*/
|
||||
void GPU_storagebuf_usage_size_set(gpu::StorageBuf *ssbo, size_t size);
|
||||
void GPU_storagebuf_update(gpu::StorageBuf *ssbo, const void *data);
|
||||
|
||||
void GPU_storagebuf_bind(gpu::StorageBuf *ssbo, int slot);
|
||||
void GPU_storagebuf_unbind(gpu::StorageBuf *ssbo);
|
||||
/**
|
||||
* Resets the internal slot usage tracking. But there is no guarantee that
|
||||
* this actually undo the bindings for the next draw call. Only has effect when G_DEBUG_GPU is set.
|
||||
*/
|
||||
void GPU_storagebuf_debug_unbind_all();
|
||||
|
||||
void GPU_storagebuf_clear_to_zero(gpu::StorageBuf *ssbo);
|
||||
|
||||
/**
|
||||
* Clear the content of the buffer using the given #clear_value. #clear_value will be used as a
|
||||
* repeatable pattern of 32bits.
|
||||
*/
|
||||
void GPU_storagebuf_clear(gpu::StorageBuf *ssbo, uint32_t clear_value);
|
||||
|
||||
/**
|
||||
* Explicitly sync updated storage buffer contents back to host within the GPU command stream. This
|
||||
* ensures any changes made by the GPU are visible to the host.
|
||||
* NOTE: This command is only valid for host-visible storage buffers.
|
||||
*/
|
||||
void GPU_storagebuf_sync_to_host(gpu::StorageBuf *ssbo);
|
||||
|
||||
/**
|
||||
* Read back content of the buffer to CPU for inspection.
|
||||
* Slow! Only use for inspection / debugging.
|
||||
*
|
||||
* NOTE: If GPU_storagebuf_sync_to_host is called, this command is synchronized against that call.
|
||||
* If pending GPU updates to the storage buffer are not yet visible to the host, the command will
|
||||
* stall until dependent GPU work has completed.
|
||||
*
|
||||
* Otherwise, this command is synchronized against this call and will stall the CPU until the
|
||||
* buffer content can be read by the host.
|
||||
*/
|
||||
void GPU_storagebuf_read(gpu::StorageBuf *ssbo, void *data);
|
||||
|
||||
/**
|
||||
* \brief Copy a part of a vertex buffer to a storage buffer.
|
||||
*
|
||||
* \param ssbo: destination storage buffer
|
||||
* \param src: source vertex buffer
|
||||
* \param dst_offset: where to start copying to (in bytes).
|
||||
* \param src_offset: where to start copying from (in bytes).
|
||||
* \param copy_size: byte size of the segment to copy.
|
||||
*/
|
||||
void GPU_storagebuf_copy_sub_from_vertbuf(
|
||||
gpu::StorageBuf *ssbo, gpu::VertBuf *src, uint dst_offset, uint src_offset, uint copy_size);
|
||||
|
||||
/**
|
||||
* Ensure the ssbo is ready to be used as an indirect buffer in `GPU_batch_draw_indirect`.
|
||||
* NOTE: Internally, this is only required for the OpenGL backend.
|
||||
*/
|
||||
void GPU_storagebuf_sync_as_indirect_buffer(gpu::StorageBuf *ssbo);
|
||||
|
||||
} // namespace blender
|
||||
1385
blender-5.2.0/source/blender/gpu/GPU_texture.hh
Normal file
1385
blender-5.2.0/source/blender/gpu/GPU_texture.hh
Normal file
File diff suppressed because it is too large
Load Diff
81
blender-5.2.0/source/blender/gpu/GPU_texture_pool.hh
Normal file
81
blender-5.2.0/source/blender/gpu/GPU_texture_pool.hh
Normal file
@@ -0,0 +1,81 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* A `gpu::TextureFromPool` is a wrapper around backend specific texture objects whose usage is
|
||||
* transient and can be shared between parts of an engine or across several parts of blender.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class TexturePool {
|
||||
public:
|
||||
virtual ~TexturePool() = default;
|
||||
|
||||
/* Return the texture pool from the active GPUContext.
|
||||
* Only valid if a context is active. */
|
||||
static TexturePool &get();
|
||||
|
||||
/* Acquire a texture of a particular type from the pool. */
|
||||
virtual Texture *acquire_texture_1d(int extent,
|
||||
int mip_len,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name = nullptr) = 0;
|
||||
virtual Texture *acquire_texture_1d_array(int extent,
|
||||
int layer_len,
|
||||
int mip_len,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name = nullptr) = 0;
|
||||
virtual Texture *acquire_texture_2d(int2 extent,
|
||||
int mip_len,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name = nullptr) = 0;
|
||||
virtual Texture *acquire_texture_2d_array(int2 extent,
|
||||
int layer_len,
|
||||
int mip_len,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name = nullptr) = 0;
|
||||
virtual Texture *acquire_texture_3d(int3 extent,
|
||||
int mip_len,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name = nullptr) = 0;
|
||||
virtual Texture *acquire_texture_cube(int extent,
|
||||
int mip_len,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name = nullptr) = 0;
|
||||
virtual Texture *acquire_texture_cube_array(int extent,
|
||||
int layer_len,
|
||||
int mip_len,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name = nullptr) = 0;
|
||||
|
||||
/* Release the texture back into the pool so it can be reused. */
|
||||
virtual void release_texture(Texture *tex) = 0;
|
||||
|
||||
/* Validate acquired texture counters and release unused textures.
|
||||
* If `force_free` is true, free unused texture memory inside the pool. */
|
||||
virtual void reset(bool force_free = false) = 0;
|
||||
|
||||
/* Modify the internal counter of an acquired texture.
|
||||
* Used by `TextureFromPool::retain()` in `DRW_gpu_wrapper.hh`. */
|
||||
virtual void offset_users_count(Texture *tex, int offset) = 0;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
69
blender-5.2.0/source/blender/gpu/GPU_uniform_buffer.hh
Normal file
69
blender-5.2.0/source/blender/gpu/GPU_uniform_buffer.hh
Normal file
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Uniform buffers API. Used to handle many uniforms update at once.
|
||||
* Make sure that the data structure is compatible with what the implementation expect.
|
||||
* (see "7.6.2.2 Standard Uniform Block Layout" from the OpenGL spec for more info about std140
|
||||
* layout)
|
||||
* Rule of thumb: Padding to 16bytes, don't use vec3, don't use arrays of anything that is not vec4
|
||||
* aligned.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUInput;
|
||||
|
||||
namespace gpu {
|
||||
class UniformBuf;
|
||||
} // namespace gpu
|
||||
|
||||
gpu::UniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name);
|
||||
/**
|
||||
* Create UBO from inputs list.
|
||||
*
|
||||
* \param inputs: ListBaseT<LinkData>
|
||||
* \return nullptr if failed to create or if `inputs` is empty.
|
||||
*/
|
||||
gpu::UniformBuf *GPU_uniformbuf_create_from_list(ListBaseT<LinkData> *inputs, const char *name);
|
||||
|
||||
#define GPU_uniformbuf_create(size) GPU_uniformbuf_create_ex(size, nullptr, __func__);
|
||||
|
||||
void GPU_uniformbuf_free(gpu::UniformBuf *ubo);
|
||||
|
||||
void GPU_uniformbuf_update(gpu::UniformBuf *ubo, const void *data);
|
||||
|
||||
void GPU_uniformbuf_bind(gpu::UniformBuf *ubo, int slot);
|
||||
void GPU_uniformbuf_bind_as_ssbo(gpu::UniformBuf *ubo, int slot);
|
||||
void GPU_uniformbuf_unbind(gpu::UniformBuf *ubo);
|
||||
/**
|
||||
* Resets the internal slot usage tracking. But there is no guarantee that
|
||||
* this actually undo the bindings for the next draw call. Only has effect when G_DEBUG_GPU is set.
|
||||
*/
|
||||
void GPU_uniformbuf_debug_unbind_all();
|
||||
|
||||
void GPU_uniformbuf_clear_to_zero(gpu::UniformBuf *ubo);
|
||||
|
||||
#define GPU_UBO_BLOCK_NAME "node_tree"
|
||||
#define GPU_ATTRIBUTE_UBO_BLOCK_NAME "unf_attrs"
|
||||
#define GPU_LAYER_ATTRIBUTE_UBO_BLOCK_NAME "drw_layer_attrs"
|
||||
constexpr static int GPU_NODE_TREE_UBO_SLOT = 0;
|
||||
|
||||
#define GPU_UBO_FREE_SAFE(ubo) \
|
||||
do { \
|
||||
if (ubo != nullptr) { \
|
||||
GPU_uniformbuf_free(ubo); \
|
||||
ubo = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
} // namespace blender
|
||||
362
blender-5.2.0/source/blender/gpu/GPU_vertex_buffer.hh
Normal file
362
blender-5.2.0/source/blender/gpu/GPU_vertex_buffer.hh
Normal file
@@ -0,0 +1,362 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU vertex buffer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "GPU_common.hh"
|
||||
#include "GPU_vertex_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
enum GPUVertBufStatus {
|
||||
/** Initial state. */
|
||||
GPU_VERTBUF_INVALID = 0,
|
||||
/** Was init with a vertex format. */
|
||||
GPU_VERTBUF_INIT = (1 << 0),
|
||||
/** Data has been touched and need to be re-uploaded. */
|
||||
GPU_VERTBUF_DATA_DIRTY = (1 << 1),
|
||||
/** The buffer has been created inside GPU memory. */
|
||||
GPU_VERTBUF_DATA_UPLOADED = (1 << 2),
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUVertBufStatus)
|
||||
|
||||
/**
|
||||
* How to create a #VertBuf:
|
||||
* 1) verts = GPU_vertbuf_calloc()
|
||||
* 2) GPU_vertformat_attr_add(verts->format, ...)
|
||||
* 3) GPU_vertbuf_data_alloc(verts, vertex_len) <-- finalizes/packs vertex format
|
||||
* 4) GPU_vertbuf_attr_fill(verts, pos, application_pos_buffer)
|
||||
*/
|
||||
|
||||
enum GPUUsageType {
|
||||
/* can be extended to support more types */
|
||||
GPU_USAGE_STREAM = 0,
|
||||
GPU_USAGE_STATIC = 1, /* do not keep data in memory */
|
||||
GPU_USAGE_DYNAMIC = 2,
|
||||
GPU_USAGE_DEVICE_ONLY = 3, /* Do not do host->device data transfers. */
|
||||
|
||||
/** Extended usage flags. */
|
||||
/* Flag for vertex buffers used for textures. Skips additional padding/compaction to ensure
|
||||
* format matches the texture exactly. Can be masked with other properties, and is stripped
|
||||
* during VertBuf::init. */
|
||||
GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY = 1 << 3,
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUUsageType);
|
||||
|
||||
namespace gpu {
|
||||
class VertBuf;
|
||||
} // namespace gpu
|
||||
|
||||
void GPU_vertbuf_discard(gpu::VertBuf *);
|
||||
|
||||
gpu::VertBuf *GPU_vertbuf_calloc();
|
||||
gpu::VertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage);
|
||||
|
||||
static inline gpu::VertBuf *GPU_vertbuf_create_with_format(const GPUVertFormat &format)
|
||||
{
|
||||
return GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_STATIC);
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class VertBufDeleter {
|
||||
public:
|
||||
void operator()(VertBuf *vbo)
|
||||
{
|
||||
GPU_vertbuf_discard(vbo);
|
||||
}
|
||||
};
|
||||
|
||||
using VertBufPtr = std::unique_ptr<gpu::VertBuf, gpu::VertBufDeleter>;
|
||||
|
||||
/**
|
||||
* Implementation of Vertex Buffers.
|
||||
* Base class which is then specialized for each implementation (GL, VK, ...).
|
||||
*/
|
||||
class VertBuf {
|
||||
public:
|
||||
static size_t memory_usage;
|
||||
|
||||
GPUVertFormat format = {};
|
||||
/** Number of verts we want to draw. */
|
||||
uint vertex_len = 0;
|
||||
/** Number of verts data. */
|
||||
uint vertex_alloc = 0;
|
||||
/** Status flag. */
|
||||
GPUVertBufStatus flag = GPU_VERTBUF_INVALID;
|
||||
|
||||
#ifndef NDEBUG
|
||||
/** Usage including extended usage flags. */
|
||||
GPUUsageType extended_usage_ = GPU_USAGE_STATIC;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/** NULL indicates data in VRAM (unmapped) */
|
||||
uchar *data_ = nullptr;
|
||||
|
||||
/** Usage hint for GL optimization. */
|
||||
GPUUsageType usage_ = GPU_USAGE_STATIC;
|
||||
|
||||
private:
|
||||
/** This counter will only avoid freeing the #VertBuf, not the data. */
|
||||
int handle_refcount_ = 1;
|
||||
|
||||
public:
|
||||
VertBuf();
|
||||
virtual ~VertBuf();
|
||||
|
||||
template<typename FormatT>
|
||||
static VertBufPtr from_size_with_format(const int size, GPUUsageType usage = GPU_USAGE_STATIC)
|
||||
{
|
||||
BLI_assert(size > 0);
|
||||
VertBufPtr buf = VertBufPtr(GPU_vertbuf_create_with_format_ex(FormatT::format(), usage));
|
||||
/* GPU formats needs to be aligned to 4 bytes. */
|
||||
buf->allocate(ceil_to_multiple_u(size * sizeof(FormatT), 4) / sizeof(FormatT));
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static VertBufPtr from_size(const int size, GPUUsageType usage = GPU_USAGE_STATIC)
|
||||
{
|
||||
return from_size_with_format<GenericVertexFormat<T>>(size, usage);
|
||||
}
|
||||
|
||||
template<typename T> static VertBufPtr from_span(const Span<T> data)
|
||||
{
|
||||
BLI_assert(!data.is_empty());
|
||||
VertBufPtr buf = VertBufPtr(GPU_vertbuf_create_with_format_ex(
|
||||
GenericVertexFormat<T>::format(), GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY));
|
||||
/* GPU formats needs to be aligned to 4 bytes. */
|
||||
buf->allocate(ceil_to_multiple_u(data.size_in_bytes(), 4) / sizeof(GenericVertexFormat<T>));
|
||||
buf->data<T>().slice(0, data.size()).copy_from(data);
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<typename T> static VertBufPtr from_varray(const VArray<T> &array)
|
||||
{
|
||||
BLI_assert(!array.is_empty());
|
||||
VertBufPtr buf = VertBufPtr(GPU_vertbuf_create_with_format_ex(
|
||||
GenericVertexFormat<T>::format(), GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY));
|
||||
/* GPU formats needs to be aligned to 4 bytes. */
|
||||
buf->allocate(ceil_to_multiple_u(array.size() * sizeof(T), 4) /
|
||||
sizeof(GenericVertexFormat<T>));
|
||||
array.materialize(buf->data<T>().slice(0, array.size()));
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<typename T> static VertBufPtr device_only(uint size)
|
||||
{
|
||||
BLI_assert(size > 0);
|
||||
VertBufPtr buf = VertBufPtr(GPU_vertbuf_create_with_format_ex(
|
||||
GenericVertexFormat<T>::format(),
|
||||
GPU_USAGE_DEVICE_ONLY | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY));
|
||||
buf->allocate(size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void init(const GPUVertFormat &format, GPUUsageType usage);
|
||||
void clear();
|
||||
|
||||
/* Data management. */
|
||||
void allocate(uint vert_len);
|
||||
void resize(uint vert_len);
|
||||
void upload();
|
||||
virtual void bind_as_ssbo(uint binding) = 0;
|
||||
virtual void bind_as_texture(uint binding) = 0;
|
||||
|
||||
virtual void wrap_handle(uint64_t handle) = 0;
|
||||
|
||||
/* Size of the data allocated. */
|
||||
size_t size_alloc_get() const
|
||||
{
|
||||
BLI_assert(this->format.packed);
|
||||
return size_t(this->vertex_alloc) * this->format.stride;
|
||||
}
|
||||
/* Size of the data uploaded to the GPU. */
|
||||
size_t size_used_get() const
|
||||
{
|
||||
BLI_assert(format.packed);
|
||||
return size_t(this->vertex_len) * this->format.stride;
|
||||
}
|
||||
|
||||
void reference_add()
|
||||
{
|
||||
handle_refcount_++;
|
||||
}
|
||||
void reference_remove()
|
||||
{
|
||||
BLI_assert(handle_refcount_ > 0);
|
||||
handle_refcount_--;
|
||||
if (handle_refcount_ == 0) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
GPUUsageType get_usage_type() const
|
||||
{
|
||||
return usage_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access to the data allocated for the vertex buffer. The size of the data type must
|
||||
* match the data type used on the GPU.
|
||||
*/
|
||||
template<typename T> MutableSpan<T> data()
|
||||
{
|
||||
return MutableSpan<uchar>(data_, this->size_alloc_get()).cast<T>();
|
||||
}
|
||||
|
||||
virtual void update_sub(uint start, uint len, const void *data) = 0;
|
||||
virtual void read(void *data) const = 0;
|
||||
|
||||
protected:
|
||||
virtual void acquire_data() = 0;
|
||||
virtual void resize_data() = 0;
|
||||
virtual void release_data() = 0;
|
||||
virtual void upload_data() = 0;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/**
|
||||
* (Download and) fill data with the data from the vertex buffer.
|
||||
* NOTE: caller is responsible to reserve enough memory of the data parameter.
|
||||
*/
|
||||
void GPU_vertbuf_read(const gpu::VertBuf *verts, void *data);
|
||||
/** Same as discard but does not free. */
|
||||
void GPU_vertbuf_clear(gpu::VertBuf *verts);
|
||||
|
||||
/**
|
||||
* Avoid gpu::VertBuf data-block being free but not its data.
|
||||
*/
|
||||
void GPU_vertbuf_handle_ref_add(gpu::VertBuf *verts);
|
||||
void GPU_vertbuf_handle_ref_remove(gpu::VertBuf *verts);
|
||||
|
||||
void GPU_vertbuf_init_with_format_ex(gpu::VertBuf &verts,
|
||||
const GPUVertFormat &format,
|
||||
GPUUsageType);
|
||||
|
||||
void GPU_vertbuf_init_build_on_device(gpu::VertBuf &verts,
|
||||
const GPUVertFormat &format,
|
||||
uint v_len);
|
||||
|
||||
gpu::VertBuf *GPU_vertbuf_create_on_device(const GPUVertFormat &format, uint v_len);
|
||||
|
||||
#define GPU_vertbuf_init_with_format(verts, format) \
|
||||
GPU_vertbuf_init_with_format_ex(verts, format, GPU_USAGE_STATIC)
|
||||
|
||||
/**
|
||||
* Create a new allocation, discarding any existing data.
|
||||
*/
|
||||
void GPU_vertbuf_data_alloc(gpu::VertBuf &verts, uint v_len);
|
||||
/**
|
||||
* Resize buffer keeping existing data.
|
||||
*/
|
||||
void GPU_vertbuf_data_resize(gpu::VertBuf &verts, uint v_len);
|
||||
/**
|
||||
* Set vertex count but does not change allocation.
|
||||
* Only this many verts will be uploaded to the GPU and rendered.
|
||||
* This is useful for streaming data.
|
||||
*/
|
||||
void GPU_vertbuf_data_len_set(gpu::VertBuf &verts, uint v_len);
|
||||
|
||||
/**
|
||||
* The most important #set_attr variant is the untyped one. Get it right first.
|
||||
* It takes a void* so the app developer is responsible for matching their app data types
|
||||
* to the vertex attribute's type and component count. They're in control of both, so this
|
||||
* should not be a problem.
|
||||
*/
|
||||
void GPU_vertbuf_attr_set(gpu::VertBuf *, uint a_idx, uint v_idx, const void *data);
|
||||
|
||||
/** Fills a whole vertex (all attributes). Data must match packed layout. */
|
||||
void GPU_vertbuf_vert_set(gpu::VertBuf *verts, uint v_idx, const void *data);
|
||||
|
||||
/**
|
||||
* Tightly packed, non interleaved input data.
|
||||
*/
|
||||
void GPU_vertbuf_attr_fill(gpu::VertBuf *, uint a_idx, const void *data);
|
||||
|
||||
void GPU_vertbuf_attr_fill_stride(gpu::VertBuf *, uint a_idx, uint stride, const void *data);
|
||||
|
||||
/**
|
||||
* For low level access only.
|
||||
*
|
||||
* \note This is obsolete, use #VertBuf::data<T>() instead.
|
||||
*/
|
||||
struct GPUVertBufRaw {
|
||||
uint size;
|
||||
uint stride;
|
||||
unsigned char *data;
|
||||
unsigned char *data_init;
|
||||
#ifndef NDEBUG
|
||||
/* Only for overflow check */
|
||||
unsigned char *_data_end;
|
||||
#endif
|
||||
};
|
||||
|
||||
GPU_INLINE void *GPU_vertbuf_raw_step(GPUVertBufRaw *a)
|
||||
{
|
||||
unsigned char *data = a->data;
|
||||
a->data += a->stride;
|
||||
#ifndef NDEBUG
|
||||
BLI_assert(data < a->_data_end);
|
||||
#endif
|
||||
return static_cast<void *>(data);
|
||||
}
|
||||
|
||||
GPU_INLINE uint GPU_vertbuf_raw_used(const GPUVertBufRaw *a)
|
||||
{
|
||||
return ((a->data - a->data_init) / a->stride);
|
||||
}
|
||||
|
||||
void GPU_vertbuf_attr_get_raw_data(gpu::VertBuf *, uint a_idx, GPUVertBufRaw *access);
|
||||
|
||||
const GPUVertFormat *GPU_vertbuf_get_format(const gpu::VertBuf *verts);
|
||||
uint GPU_vertbuf_get_vertex_alloc(const gpu::VertBuf *verts);
|
||||
uint GPU_vertbuf_get_vertex_len(const gpu::VertBuf *verts);
|
||||
GPUVertBufStatus GPU_vertbuf_get_status(const gpu::VertBuf *verts);
|
||||
void GPU_vertbuf_tag_dirty(gpu::VertBuf *verts);
|
||||
|
||||
/**
|
||||
* Should be rename to #GPU_vertbuf_data_upload.
|
||||
*/
|
||||
void GPU_vertbuf_use(gpu::VertBuf *);
|
||||
void GPU_vertbuf_bind_as_ssbo(gpu::VertBuf *verts, int binding);
|
||||
void GPU_vertbuf_bind_as_texture(gpu::VertBuf *verts, int binding);
|
||||
|
||||
void GPU_vertbuf_wrap_handle(gpu::VertBuf *verts, uint64_t handle);
|
||||
|
||||
/**
|
||||
* XXX: do not use!
|
||||
* This is just a wrapper for the use of the Hair refine workaround.
|
||||
* To be used with #GPU_vertbuf_use().
|
||||
*/
|
||||
void GPU_vertbuf_update_sub(gpu::VertBuf *verts, uint start, uint len, const void *data);
|
||||
|
||||
/* Metrics */
|
||||
uint GPU_vertbuf_get_memory_usage();
|
||||
|
||||
/* Macros */
|
||||
#define GPU_VERTBUF_DISCARD_SAFE(verts) \
|
||||
do { \
|
||||
if (verts != nullptr) { \
|
||||
GPU_vertbuf_discard(verts); \
|
||||
verts = nullptr; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
} // namespace blender
|
||||
412
blender-5.2.0/source/blender/gpu/GPU_vertex_format.hh
Normal file
412
blender-5.2.0/source/blender/gpu/GPU_vertex_format.hh
Normal file
@@ -0,0 +1,412 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU vertex format
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "GPU_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
|
||||
enum class VertAttrType : uint8_t {
|
||||
Invalid = 0,
|
||||
|
||||
#define DECLARE(a, b, c, blender_enum, d, e, f, g, h) blender_enum = int(DataFormat::blender_enum),
|
||||
|
||||
#define GPU_VERTEX_FORMAT_EXPAND(impl) \
|
||||
SNORM_8_8_8_8_(impl) \
|
||||
\
|
||||
SNORM_16_16_(impl) \
|
||||
SNORM_16_16_16_16_(impl) \
|
||||
\
|
||||
UNORM_8_8_8_8_(impl) \
|
||||
\
|
||||
UNORM_16_16_(impl) \
|
||||
UNORM_16_16_16_16_(impl) \
|
||||
\
|
||||
SINT_8_8_8_8_(impl) \
|
||||
\
|
||||
SINT_16_16_(impl) \
|
||||
SINT_16_16_16_16_(impl) \
|
||||
\
|
||||
SINT_32_(impl) \
|
||||
SINT_32_32_(impl) \
|
||||
SINT_32_32_32_(impl) \
|
||||
SINT_32_32_32_32_(impl) \
|
||||
\
|
||||
UINT_8_8_8_8_(impl) \
|
||||
\
|
||||
UINT_16_16_(impl) \
|
||||
UINT_16_16_16_16_(impl) \
|
||||
\
|
||||
UINT_32_(impl) \
|
||||
UINT_32_32_(impl) \
|
||||
UINT_32_32_32_(impl) \
|
||||
UINT_32_32_32_32_(impl) \
|
||||
\
|
||||
SFLOAT_32_(impl) \
|
||||
SFLOAT_32_32_(impl) \
|
||||
SFLOAT_32_32_32_(impl) \
|
||||
SFLOAT_32_32_32_32_(impl) \
|
||||
\
|
||||
SNORM_10_10_10_2_(impl) \
|
||||
UNORM_10_10_10_2_(impl) \
|
||||
\
|
||||
/* UFLOAT_11_11_10_(impl) Available on Metal (and maybe VK) but not on GL. */ \
|
||||
/* UFLOAT_9_9_9_EXP_5_(impl) Available on Metal (and maybe VK) but not on GL. */
|
||||
|
||||
GPU_VERTEX_FORMAT_EXPAND(DECLARE)
|
||||
#undef DECLARE
|
||||
|
||||
#define DECLARE(a, b, c, blender_enum, d, e, f, g, h) \
|
||||
blender_enum##_DEPRECATED = int(DataFormat::blender_enum),
|
||||
|
||||
/* Deprecated formats. To be removed in 5.0. Needed for python shaders. */
|
||||
#define GPU_VERTEX_DEPRECATED_FORMAT_EXPAND(impl) \
|
||||
SNORM_8_(impl) \
|
||||
SNORM_8_8_(impl) \
|
||||
SNORM_8_8_8_(impl) \
|
||||
SNORM_16_(impl) \
|
||||
SNORM_16_16_16_(impl) \
|
||||
UNORM_8_(impl) \
|
||||
UNORM_8_8_(impl) \
|
||||
UNORM_8_8_8_(impl) \
|
||||
UNORM_16_(impl) \
|
||||
UNORM_16_16_16_(impl) \
|
||||
SINT_8_(impl) \
|
||||
SINT_8_8_(impl) \
|
||||
SINT_8_8_8_(impl) \
|
||||
SINT_16_(impl) \
|
||||
SINT_16_16_16_(impl) \
|
||||
UINT_8_(impl) \
|
||||
UINT_8_8_(impl) \
|
||||
UINT_8_8_8_(impl) \
|
||||
UINT_16_(impl) \
|
||||
UINT_16_16_16_(impl)
|
||||
|
||||
GPU_VERTEX_DEPRECATED_FORMAT_EXPAND(DECLARE)
|
||||
|
||||
#undef DECLARE
|
||||
};
|
||||
|
||||
/* TODO: Should reuse GPU_VERTEX_FORMAT_EXPAND, but we need to have `s/unorm` types first. */
|
||||
#define GPU_VERTEX_FORMAT_EXPAND_TYPED(impl) \
|
||||
SINT_8_8_8_8_(impl) \
|
||||
\
|
||||
SINT_16_16_(impl) \
|
||||
SINT_16_16_16_16_(impl) \
|
||||
\
|
||||
SINT_32_(impl) \
|
||||
SINT_32_32_(impl) \
|
||||
SINT_32_32_32_(impl) \
|
||||
SINT_32_32_32_32_(impl) \
|
||||
\
|
||||
UINT_8_8_8_8_(impl) \
|
||||
\
|
||||
UINT_16_16_(impl) \
|
||||
UINT_16_16_16_16_(impl) \
|
||||
\
|
||||
UINT_32_(impl) \
|
||||
UINT_32_32_(impl) \
|
||||
UINT_32_32_32_(impl) \
|
||||
UINT_32_32_32_32_(impl) \
|
||||
\
|
||||
SFLOAT_32_(impl) \
|
||||
SFLOAT_32_32_(impl) \
|
||||
SFLOAT_32_32_32_(impl) \
|
||||
SFLOAT_32_32_32_32_(impl) \
|
||||
\
|
||||
/* UFLOAT_11_11_10_(impl) Available on Metal (and maybe VK) but not on GL. */ \
|
||||
/* UFLOAT_9_9_9_EXP_5_(impl) Available on Metal (and maybe VK) but not on GL. */
|
||||
|
||||
/* Must be implemented for each type used in vertex format.
|
||||
* Should contain the associated VertAttrType just like below. */
|
||||
template<typename T> struct AttrType {};
|
||||
|
||||
#define ATTR_TYPE_MAPPING(_type, b, c, blender_enum, d, e, f, g, h) \
|
||||
template<> struct AttrType<_type> { \
|
||||
static constexpr VertAttrType type = VertAttrType::blender_enum; \
|
||||
};
|
||||
|
||||
GPU_VERTEX_FORMAT_EXPAND_TYPED(ATTR_TYPE_MAPPING)
|
||||
|
||||
#undef ATTR_TYPE_MAPPING
|
||||
|
||||
constexpr DataFormat to_data_format(VertAttrType format)
|
||||
{
|
||||
return DataFormat(int(format));
|
||||
}
|
||||
|
||||
class Shader;
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
constexpr static int GPU_VERT_ATTR_MAX_LEN = 16;
|
||||
constexpr static int GPU_VERT_ATTR_MAX_NAMES = 6;
|
||||
constexpr static int GPU_VERT_ATTR_NAMES_BUF_LEN = 256;
|
||||
constexpr static int GPU_VERT_FORMAT_MAX_NAMES = 63; /* More than enough, actual max is ~30. */
|
||||
/* Computed as GPU_VERT_ATTR_NAMES_BUF_LEN / 30 (actual max format name). */
|
||||
constexpr static int GPU_MAX_SAFE_ATTR_NAME = 12;
|
||||
|
||||
enum GPUVertCompType {
|
||||
GPU_COMP_I8 = 0,
|
||||
GPU_COMP_U8,
|
||||
GPU_COMP_I16,
|
||||
GPU_COMP_U16,
|
||||
GPU_COMP_I32,
|
||||
GPU_COMP_U32,
|
||||
|
||||
GPU_COMP_F32,
|
||||
|
||||
GPU_COMP_I10,
|
||||
/* Warning! adjust GPUVertAttr if changing. */
|
||||
|
||||
GPU_COMP_MAX
|
||||
};
|
||||
|
||||
enum GPUVertFetchMode {
|
||||
GPU_FETCH_FLOAT = 0,
|
||||
GPU_FETCH_INT,
|
||||
GPU_FETCH_INT_TO_FLOAT_UNIT, /* 127 (ubyte) -> 0.5 (and so on for other int types) */
|
||||
/* Warning! adjust GPUVertAttr if changing. */
|
||||
};
|
||||
|
||||
struct GPUVertAttr {
|
||||
/* To replace fetch_mode, comp_type, comp_len, size. */
|
||||
struct Type {
|
||||
gpu::VertAttrType format;
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return to_bytesize(to_data_format(format));
|
||||
};
|
||||
|
||||
int comp_len() const
|
||||
{
|
||||
return format_component_len(to_data_format(format));
|
||||
}
|
||||
|
||||
GPUVertFetchMode fetch_mode() const;
|
||||
GPUVertCompType comp_type() const;
|
||||
} type;
|
||||
/* from beginning of vertex, in bytes */
|
||||
uint8_t offset;
|
||||
/* up to GPU_VERT_ATTR_MAX_NAMES */
|
||||
uint8_t name_len;
|
||||
uchar names[GPU_VERT_ATTR_MAX_NAMES];
|
||||
};
|
||||
|
||||
BLI_STATIC_ASSERT(GPU_VERT_ATTR_NAMES_BUF_LEN <= 256,
|
||||
"We use uchar as index inside the name buffer "
|
||||
"so GPU_VERT_ATTR_NAMES_BUF_LEN needs to be "
|
||||
"smaller than GPUVertFormat->name_offset and "
|
||||
"GPUVertAttr->names maximum value");
|
||||
|
||||
struct GPUVertFormat {
|
||||
/** 0 to 16 (GPU_VERT_ATTR_MAX_LEN). */
|
||||
uint attr_len : 5;
|
||||
/** Total count of active vertex attribute names. (max GPU_VERT_FORMAT_MAX_NAMES) */
|
||||
uint name_len : 6;
|
||||
/** Stride in bytes, 1 to 1024. */
|
||||
uint stride : 11;
|
||||
/** Has the format been packed. */
|
||||
uint packed : 1;
|
||||
/** Current offset in names[]. */
|
||||
uint name_offset : 8;
|
||||
/** Store each attribute in one contiguous buffer region. */
|
||||
uint deinterleaved : 1;
|
||||
|
||||
GPUVertAttr attrs[GPU_VERT_ATTR_MAX_LEN];
|
||||
char names[GPU_VERT_ATTR_NAMES_BUF_LEN];
|
||||
|
||||
void pack();
|
||||
uint attribute_add(StringRef name, gpu::VertAttrType type, size_t offset = -1);
|
||||
};
|
||||
|
||||
#define GPU_VERTEX_FORMAT_ADD_ATTR(attr) \
|
||||
format.attribute_add(#attr, gpu::AttrType<decltype(attr)>::type, offsetof(VertT, attr)); \
|
||||
BLI_STATIC_ASSERT(offsetof(VertT, attr) < 255, #attr " has offset greater than 255") \
|
||||
BLI_STATIC_ASSERT(offsetof(VertT, attr) % 4 == 0, #attr " is not aligned to 4 bytes")
|
||||
|
||||
#define _ATTR_EXPAND1(a) GPU_VERTEX_FORMAT_ADD_ATTR(a)
|
||||
#define _ATTR_EXPAND2(a, b) \
|
||||
_ATTR_EXPAND1(a) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(b)
|
||||
#define _ATTR_EXPAND3(a, b, c) \
|
||||
_ATTR_EXPAND2(a, b) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(c)
|
||||
#define _ATTR_EXPAND4(a, b, c, d) \
|
||||
_ATTR_EXPAND3(a, b, c) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(d)
|
||||
#define _ATTR_EXPAND5(a, b, c, d, e) \
|
||||
_ATTR_EXPAND4(a, b, c, d) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(e)
|
||||
#define _ATTR_EXPAND6(a, b, c, d, e, f) \
|
||||
_ATTR_EXPAND5(a, b, c, d, e) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(f)
|
||||
#define _ATTR_EXPAND7(a, b, c, d, e, f, g) \
|
||||
_ATTR_EXPAND6(a, b, c, d, e, f) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(g)
|
||||
#define _ATTR_EXPAND8(a, b, c, d, e, f, g, h) \
|
||||
_ATTR_EXPAND7(a, b, c, d, e, f, g) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(h)
|
||||
#define _ATTR_EXPAND9(a, b, c, d, e, f, g, h, i) \
|
||||
_ATTR_EXPAND8(a, b, c, d, e, f, g, h) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(i)
|
||||
#define _ATTR_EXPAND10(a, b, c, d, e, f, g, h, i, j) \
|
||||
_ATTR_EXPAND9(a, b, c, d, e, f, g, h, i) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(j)
|
||||
#define _ATTR_EXPAND11(a, b, c, d, e, f, g, h, i, j, k) \
|
||||
_ATTR_EXPAND10(a, b, c, d, e, f, g, h, i, j) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(k)
|
||||
#define _ATTR_EXPAND12(a, b, c, d, e, f, g, h, i, j, k, l) \
|
||||
_ATTR_EXPAND11(a, b, c, d, e, f, g, h, i, j, k) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(l)
|
||||
#define _ATTR_EXPAND13(a, b, c, d, e, f, g, h, i, j, k, l, m) \
|
||||
_ATTR_EXPAND12(a, b, c, d, e, f, g, h, i, j, k, l) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(m)
|
||||
#define _ATTR_EXPAND14(a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
|
||||
_ATTR_EXPAND13(a, b, c, d, e, f, g, h, i, j, k, l, m) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(n)
|
||||
#define _ATTR_EXPAND15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
|
||||
_ATTR_EXPAND14(a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(o)
|
||||
#define _ATTR_EXPAND16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
|
||||
_ATTR_EXPAND15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR(p)
|
||||
/* We only support up to GPU_VERT_ATTR_MAX_LEN attribute per format. */
|
||||
|
||||
#define GPU_VERTEX_FORMAT_ADD_ATTR_EXPAND(...) VA_NARGS_CALL_OVERLOAD(_ATTR_EXPAND, __VA_ARGS__)
|
||||
|
||||
#define GPU_VERTEX_FORMAT_FUNC(_VertT, ...) \
|
||||
static GPUVertFormat &format() \
|
||||
{ \
|
||||
using VertT = _VertT; \
|
||||
static GPUVertFormat format = {}; \
|
||||
if (format.attr_len == 0) { \
|
||||
GPU_VERTEX_FORMAT_ADD_ATTR_EXPAND(__VA_ARGS__) \
|
||||
format.stride = sizeof(VertT); \
|
||||
BLI_STATIC_ASSERT(sizeof(VertT) < 1024, "Vertex format is too big") \
|
||||
format.packed = true; \
|
||||
BLI_STATIC_ASSERT_ALIGN(VertT, 4) \
|
||||
} \
|
||||
return format; \
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/** Generic vertex format for single attribute buffers. */
|
||||
template<typename T> struct GenericVertexFormat {
|
||||
T attr;
|
||||
GPU_VERTEX_FORMAT_FUNC(GenericVertexFormat, attr);
|
||||
};
|
||||
|
||||
template<> struct GenericVertexFormat<int8_t> {
|
||||
/* This is a workaround to reinterpret int8_t into padded vertex format to be able to upload it
|
||||
* on any GPU. The shaders then need to read uint32_t and use shifts and mask to decode in
|
||||
* individual bytes. */
|
||||
uint32_t attr;
|
||||
GPU_VERTEX_FORMAT_FUNC(GenericVertexFormat, attr);
|
||||
};
|
||||
|
||||
template<> struct GenericVertexFormat<uint8_t> {
|
||||
/* This is a workaround to reinterpret uint8_t into padded vertex format to be able to upload it
|
||||
* on any GPU. The shaders then need to read uint32_t and use shifts and mask to decode in
|
||||
* individual bytes. */
|
||||
uint32_t attr;
|
||||
GPU_VERTEX_FORMAT_FUNC(GenericVertexFormat, attr);
|
||||
};
|
||||
|
||||
template<> struct GenericVertexFormat<bool> {
|
||||
/* This is a workaround to reinterpret bool into padded vertex format to be able to upload it
|
||||
* on any GPU. The shaders then need to read uint32_t and use shifts and mask to decode in
|
||||
* individual bytes. */
|
||||
uint32_t attr;
|
||||
GPU_VERTEX_FORMAT_FUNC(GenericVertexFormat, attr);
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
void GPU_vertformat_clear(GPUVertFormat *);
|
||||
void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat &src);
|
||||
void GPU_vertformat_from_shader(GPUVertFormat *format, const gpu::Shader *shader);
|
||||
|
||||
uint GPU_vertformat_attr_add(GPUVertFormat *format, StringRef name, gpu::VertAttrType type);
|
||||
/* Legacy/unsafe version.
|
||||
* TODO: Replace by vertex_format_combine. */
|
||||
uint GPU_vertformat_attr_add_legacy(
|
||||
GPUVertFormat *, StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode);
|
||||
|
||||
void GPU_vertformat_alias_add(GPUVertFormat *, StringRef alias);
|
||||
|
||||
/**
|
||||
* Return a vertex format from a single attribute description.
|
||||
* The attribute ID is ensured to be 0.
|
||||
*/
|
||||
GPUVertFormat GPU_vertformat_from_attribute(StringRef name, gpu::VertAttrType type);
|
||||
|
||||
/**
|
||||
* Makes vertex attribute from the next vertices to be accessible in the vertex shader.
|
||||
* For an attribute named "attr" you can access the next nth vertex using "attr{number}".
|
||||
* Use this function after specifying all the attributes in the format.
|
||||
*
|
||||
* NOTE: This does NOT work when using indexed rendering.
|
||||
* NOTE: Only works for first attribute name. (this limitation can be changed if needed)
|
||||
*
|
||||
* WARNING: this function creates a lot of aliases/attributes, make sure to keep the attribute
|
||||
* name short to avoid overflowing the name-buffer.
|
||||
*/
|
||||
void GPU_vertformat_multiload_enable(GPUVertFormat *format, int load_count);
|
||||
|
||||
/**
|
||||
* Make attribute layout non-interleaved.
|
||||
* Warning! This does not change data layout!
|
||||
* Use direct buffer access to fill the data.
|
||||
* This is for advanced usage.
|
||||
*
|
||||
* De-interleaved data means all attribute data for each attribute
|
||||
* is stored continuously like this:
|
||||
* 000011112222
|
||||
* instead of:
|
||||
* 012012012012
|
||||
*
|
||||
* \note This is per attribute de-interleaving, NOT per component.
|
||||
*/
|
||||
void GPU_vertformat_deinterleave(GPUVertFormat *format);
|
||||
|
||||
int GPU_vertformat_attr_id_get(const GPUVertFormat *, StringRef name);
|
||||
|
||||
BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format,
|
||||
const GPUVertAttr *attr,
|
||||
uint n_idx)
|
||||
{
|
||||
return format->names + attr->names[n_idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* \warning Can only rename using a string with same character count.
|
||||
* \warning This removes all other aliases of this attribute.
|
||||
*/
|
||||
void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr, const char *new_name);
|
||||
|
||||
/**
|
||||
* \warning Always add a prefix to the result of this function as
|
||||
* the generated string can start with a number and not be a valid attribute name.
|
||||
*/
|
||||
void GPU_vertformat_safe_attr_name(StringRef attr_name, char *r_safe_name, uint max_len);
|
||||
|
||||
} // namespace blender
|
||||
96
blender-5.2.0/source/blender/gpu/GPU_viewport.hh
Normal file
96
blender-5.2.0/source/blender/gpu/GPU_viewport.hh
Normal file
@@ -0,0 +1,96 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define GLA_PIXEL_OFS 0.375f
|
||||
|
||||
struct DRWData;
|
||||
struct GPUViewport;
|
||||
struct GPUOffScreen;
|
||||
namespace gpu {
|
||||
class FrameBuffer;
|
||||
} // namespace gpu
|
||||
|
||||
GPUViewport *GPU_viewport_create();
|
||||
GPUViewport *GPU_viewport_stereo_create();
|
||||
void GPU_viewport_bind(GPUViewport *viewport, int view, const rcti *rect);
|
||||
void GPU_viewport_unbind(GPUViewport *viewport);
|
||||
/**
|
||||
* Merge and draw the buffers of \a viewport into the currently active framebuffer, performing
|
||||
* color transform to display space.
|
||||
*
|
||||
* \param rect: Coordinates to draw into. By swapping min and max values, drawing can be done
|
||||
* with inversed axis coordinates (upside down or sideways).
|
||||
*/
|
||||
void GPU_viewport_draw_to_screen(GPUViewport *viewport, int view, const rcti *rect);
|
||||
/**
|
||||
* Version of #GPU_viewport_draw_to_screen() that lets caller decide if display colorspace
|
||||
* transform should be performed.
|
||||
*/
|
||||
void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport,
|
||||
int view,
|
||||
const rcti *rect,
|
||||
bool display_colorspace,
|
||||
bool do_overlay_merge);
|
||||
/**
|
||||
* Must be executed inside Draw-manager OpenGL Context.
|
||||
*/
|
||||
void GPU_viewport_free(GPUViewport *viewport);
|
||||
|
||||
void GPU_viewport_colorspace_set(GPUViewport *viewport,
|
||||
const ColorManagedViewSettings *view_settings,
|
||||
const ColorManagedDisplaySettings *display_settings,
|
||||
float dither);
|
||||
|
||||
/**
|
||||
* Should be called from DRW after DRW_gpu_context_enable.
|
||||
*/
|
||||
void GPU_viewport_bind_from_offscreen(GPUViewport *viewport,
|
||||
GPUOffScreen *ofs,
|
||||
bool is_xr_surface);
|
||||
/**
|
||||
* Clear vars assigned from offscreen, so we don't free data owned by `GPUOffScreen`.
|
||||
*/
|
||||
void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport,
|
||||
GPUOffScreen *ofs,
|
||||
bool display_colorspace,
|
||||
bool do_overlay_merge);
|
||||
|
||||
DRWData **GPU_viewport_data_get(GPUViewport *viewport);
|
||||
|
||||
/**
|
||||
* Merge the stereo textures. `color` and `overlay` texture will be modified.
|
||||
*/
|
||||
void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo_format);
|
||||
|
||||
void GPU_viewport_tag_update(GPUViewport *viewport);
|
||||
bool GPU_viewport_do_update(GPUViewport *viewport);
|
||||
|
||||
int GPU_viewport_active_view_get(GPUViewport *viewport);
|
||||
bool GPU_viewport_is_stereo_get(GPUViewport *viewport);
|
||||
|
||||
gpu::Texture *GPU_viewport_color_texture(GPUViewport *viewport, int view);
|
||||
gpu::Texture *GPU_viewport_overlay_texture(GPUViewport *viewport, int view);
|
||||
gpu::Texture *GPU_viewport_depth_texture(GPUViewport *viewport);
|
||||
|
||||
/**
|
||||
* Color render and overlay frame-buffers for drawing outside of DRW module.
|
||||
*/
|
||||
gpu::FrameBuffer *GPU_viewport_framebuffer_render_get(GPUViewport *viewport);
|
||||
gpu::FrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport);
|
||||
|
||||
} // namespace blender
|
||||
59
blender-5.2.0/source/blender/gpu/GPU_worker.hh
Normal file
59
blender-5.2.0/source/blender/gpu/GPU_worker.hh
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector.hh"
|
||||
#include "GPU_context.hh"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
using WorkCallback = void (*)(void *);
|
||||
using WorkID = uint64_t;
|
||||
|
||||
/**
|
||||
* Abstracts the creation and management of secondary threads with GPU contexts.
|
||||
* Must be created from the main thread.
|
||||
* Threads and their context remain alive until destruction.
|
||||
*/
|
||||
class GPUWorker {
|
||||
private:
|
||||
Vector<std::unique_ptr<std::thread>> threads_;
|
||||
ThreadQueue *work_queue_;
|
||||
WorkCallback callback_;
|
||||
|
||||
public:
|
||||
enum class ContextType {
|
||||
/** Use the main GPU context on the worker threads. */
|
||||
Main,
|
||||
/** Use a different secondary GPU context for each worker thread. */
|
||||
PerThread,
|
||||
};
|
||||
|
||||
/**
|
||||
* \param threads_count: Number of threads to span.
|
||||
* \param context_type: The type of context each thread uses.
|
||||
* \param callback: The callback function that will be called for each acquired work
|
||||
* (passed as a void pointer).
|
||||
*/
|
||||
GPUWorker(uint32_t threads_count, ContextType context_type, WorkCallback callback);
|
||||
~GPUWorker();
|
||||
|
||||
WorkID push_work(void *work, ThreadQueueWorkPriority priority);
|
||||
bool cancel_work(WorkID id);
|
||||
bool is_empty();
|
||||
|
||||
private:
|
||||
void run(std::shared_ptr<GPUSecondaryContext> context);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
102
blender-5.2.0/source/blender/gpu/dummy/dummy_backend.hh
Normal file
102
blender-5.2.0/source/blender/gpu/dummy/dummy_backend.hh
Normal file
@@ -0,0 +1,102 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_platform_private.hh"
|
||||
|
||||
#include "dummy_batch.hh"
|
||||
#include "dummy_context.hh"
|
||||
#include "dummy_framebuffer.hh"
|
||||
#include "dummy_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class DummyBackend : public GPUBackend {
|
||||
public:
|
||||
DummyBackend()
|
||||
{
|
||||
GPG.init(GPU_DEVICE_ANY,
|
||||
GPU_OS_ANY,
|
||||
GPU_DRIVER_ANY,
|
||||
GPU_SUPPORT_LEVEL_UNSUPPORTED,
|
||||
GPU_BACKEND_NONE,
|
||||
"Unknown",
|
||||
"",
|
||||
"",
|
||||
GPU_ARCHITECTURE_IMR);
|
||||
}
|
||||
void init_resources() override {}
|
||||
void delete_resources() override {}
|
||||
void compute_dispatch(int /*groups_x_len*/, int /*groups_y_len*/, int /*groups_z_len*/) override
|
||||
{
|
||||
}
|
||||
void compute_dispatch_indirect(StorageBuf * /*indirect_buf*/) override {}
|
||||
Context *context_alloc(GHOST_IWindow * /*ghost_window*/,
|
||||
GHOST_IContext * /*ghost_context*/) override
|
||||
{
|
||||
return new DummyContext;
|
||||
}
|
||||
Batch *batch_alloc() override
|
||||
{
|
||||
return new DummyBatch;
|
||||
}
|
||||
Fence *fence_alloc() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
FrameBuffer *framebuffer_alloc(const char *name) override
|
||||
{
|
||||
return new DummyFrameBuffer(name);
|
||||
}
|
||||
IndexBuf *indexbuf_alloc() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
PixelBuffer *pixelbuf_alloc(size_t /*size*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
QueryPool *querypool_alloc() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
Shader *shader_alloc(const char * /*name*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
Texture *texture_alloc(const char * /*name*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
TexturePool *texturepool_alloc() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
UniformBuf *uniformbuf_alloc(size_t /*size*/, const char * /*name*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
StorageBuf *storagebuf_alloc(size_t /*size*/,
|
||||
GPUUsageType /*usage*/,
|
||||
const char * /*name*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
VertBuf *vertbuf_alloc() override
|
||||
{
|
||||
return new DummyVertexBuffer;
|
||||
}
|
||||
void shader_cache_dir_clear_old() override {}
|
||||
void render_begin() override {}
|
||||
void render_end() override {}
|
||||
void render_step(bool /*force_resource_release*/) override {}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
32
blender-5.2.0/source/blender/gpu/dummy/dummy_batch.hh
Normal file
32
blender-5.2.0/source/blender/gpu/dummy/dummy_batch.hh
Normal file
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class DummyBatch : public Batch {
|
||||
public:
|
||||
void draw(int /*vertex_first*/,
|
||||
int /*vertex_count*/,
|
||||
int /*instance_first*/,
|
||||
int /*instance_count*/) override
|
||||
{
|
||||
}
|
||||
void draw_indirect(StorageBuf * /*indirect_buf*/, intptr_t /*offset*/) override {}
|
||||
void multi_draw_indirect(StorageBuf * /*indirect_buf*/,
|
||||
int /*count*/,
|
||||
intptr_t /*offset*/,
|
||||
intptr_t /*stride*/) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
58
blender-5.2.0/source/blender/gpu/dummy/dummy_context.hh
Normal file
58
blender-5.2.0/source/blender/gpu/dummy/dummy_context.hh
Normal file
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "dummy_framebuffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class DummyContext : public Context {
|
||||
public:
|
||||
DummyContext()
|
||||
{
|
||||
back_left = active_fb = new DummyFrameBuffer("DummyFramebuffer");
|
||||
}
|
||||
~DummyContext() override
|
||||
{
|
||||
free_resources();
|
||||
}
|
||||
void activate() override {}
|
||||
void deactivate() override {}
|
||||
void begin_frame() override {}
|
||||
void end_frame() override {}
|
||||
|
||||
void flush() override {}
|
||||
void finish() override {}
|
||||
|
||||
void memory_statistics_get(int * /*r_total_mem*/, int * /*r_free_mem*/) override {}
|
||||
|
||||
void debug_group_begin(const char * /*unused*/, int /*unused*/) override {}
|
||||
void debug_group_end() override {}
|
||||
bool debug_capture_begin(const char * /*title*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void debug_capture_end() override {}
|
||||
void *debug_capture_scope_create(const char * /*name*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
bool debug_capture_scope_begin(void * /*scope*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void debug_capture_scope_end(void * /*scope*/) override {}
|
||||
|
||||
void debug_unbind_all_ubo() override {}
|
||||
void debug_unbind_all_ssbo() override {}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
56
blender-5.2.0/source/blender/gpu/dummy/dummy_framebuffer.hh
Normal file
56
blender-5.2.0/source/blender/gpu/dummy/dummy_framebuffer.hh
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_framebuffer_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class DummyFrameBuffer : public FrameBuffer {
|
||||
public:
|
||||
DummyFrameBuffer(const char *name) : FrameBuffer(name) {}
|
||||
void bind(bool /*enabled_srgb*/) override {}
|
||||
bool check(char /*err_out*/[256]) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void clear(GPUFrameBufferBits /*buffers*/,
|
||||
const double4 /*clear_color*/,
|
||||
float /*clear_depth*/,
|
||||
uint /*clear_stencil*/) override
|
||||
{
|
||||
}
|
||||
void clear_multi(Span<double4> /*clear_cols*/) override {}
|
||||
void clear_attachment(GPUAttachmentType /*type*/, const double4 /*clear_value*/) override {}
|
||||
|
||||
void attachment_set_loadstore_op(GPUAttachmentType /*type*/, GPULoadStore /*ls*/) override {}
|
||||
|
||||
void subpass_transition_impl(const GPUAttachmentState /*depth_attachment_state*/,
|
||||
Span<GPUAttachmentState> /*color_attachment_states*/) override {};
|
||||
|
||||
void read(GPUFrameBufferBits /*planes*/,
|
||||
eGPUDataFormat /*format*/,
|
||||
const int /*area*/[4],
|
||||
int /*channel_len*/,
|
||||
int /*slot*/,
|
||||
void * /*r_data*/) override
|
||||
{
|
||||
}
|
||||
|
||||
void blit_to(GPUFrameBufferBits /*planes*/,
|
||||
int /*src_slot*/,
|
||||
FrameBuffer * /*dst*/,
|
||||
int /*dst_slot*/,
|
||||
int /*dst_offset_x*/,
|
||||
int /*dst_offset_y*/) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class DummyVertexBuffer : public VertBuf {
|
||||
|
||||
public:
|
||||
void bind_as_ssbo(uint /*binding*/) override {}
|
||||
void bind_as_texture(uint /*binding*/) override {}
|
||||
void wrap_handle(uint64_t /*handle*/) override {}
|
||||
|
||||
void update_sub(uint /*start*/, uint /*len*/, const void * /*data*/) override {}
|
||||
void read(void * /*data*/) const override {}
|
||||
|
||||
protected:
|
||||
void acquire_data() override
|
||||
{
|
||||
MEM_SAFE_DELETE(data_);
|
||||
data_ = MEM_new_array_uninitialized<uchar>(this->size_alloc_get(), __func__);
|
||||
}
|
||||
void resize_data() override {}
|
||||
void release_data() override
|
||||
{
|
||||
MEM_SAFE_DELETE(data_);
|
||||
}
|
||||
void upload_data() override {}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
119
blender-5.2.0/source/blender/gpu/intern/gpu_backend.hh
Normal file
119
blender-5.2.0/source/blender/gpu/intern/gpu_backend.hh
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPUBackend derived class contain allocators that do not need a context bound.
|
||||
* The backend is init at startup and is accessible using GPU_backend_get() */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "GHOST_IContext.hh"
|
||||
#include "GHOST_IWindow.hh"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class Context;
|
||||
|
||||
class Batch;
|
||||
class Fence;
|
||||
class FrameBuffer;
|
||||
class IndexBuf;
|
||||
class PixelBuffer;
|
||||
class QueryPool;
|
||||
class Shader;
|
||||
class ShaderCompiler;
|
||||
class Texture;
|
||||
class TexturePool;
|
||||
class UniformBuf;
|
||||
class StorageBuf;
|
||||
class VertBuf;
|
||||
|
||||
class GPUBackend {
|
||||
protected:
|
||||
ShaderCompiler *compiler_;
|
||||
|
||||
public:
|
||||
virtual ~GPUBackend() = default;
|
||||
|
||||
/* Called after the main context creation and activation. */
|
||||
virtual void init_resources() = 0;
|
||||
/* Called before the main context deletion and deactivation. */
|
||||
virtual void delete_resources() = 0;
|
||||
|
||||
static GPUBackend *get();
|
||||
|
||||
ShaderCompiler *get_compiler()
|
||||
{
|
||||
return compiler_;
|
||||
}
|
||||
|
||||
virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0;
|
||||
virtual void compute_dispatch_indirect(StorageBuf *indirect_buf) = 0;
|
||||
|
||||
virtual Context *context_alloc(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context) = 0;
|
||||
|
||||
virtual Batch *batch_alloc() = 0;
|
||||
virtual Fence *fence_alloc() = 0;
|
||||
virtual FrameBuffer *framebuffer_alloc(const char *name) = 0;
|
||||
virtual IndexBuf *indexbuf_alloc() = 0;
|
||||
virtual PixelBuffer *pixelbuf_alloc(size_t size) = 0;
|
||||
virtual QueryPool *querypool_alloc() = 0;
|
||||
virtual Shader *shader_alloc(const char *name) = 0;
|
||||
virtual Texture *texture_alloc(const char *name) = 0;
|
||||
virtual TexturePool *texturepool_alloc() = 0;
|
||||
virtual UniformBuf *uniformbuf_alloc(size_t size, const char *name) = 0;
|
||||
virtual StorageBuf *storagebuf_alloc(size_t size, GPUUsageType usage, const char *name) = 0;
|
||||
virtual VertBuf *vertbuf_alloc() = 0;
|
||||
virtual void shader_cache_dir_clear_old() = 0;
|
||||
|
||||
/* Render Frame Coordination --
|
||||
* Used for performing per-frame actions globally */
|
||||
virtual void render_begin() = 0;
|
||||
virtual void render_end() = 0;
|
||||
virtual void render_step(bool force_resource_release = false) = 0;
|
||||
};
|
||||
|
||||
namespace debug {
|
||||
static ColorTheme4f GPU_DEBUG_GROUP_COLOR_DEFAULT = {};
|
||||
|
||||
static inline ColorTheme4f get_debug_group_color(StringRefNull name)
|
||||
{
|
||||
if (name == "EEVEE") {
|
||||
return ColorTheme4f(1.0, 0.5, 0.0, 1.0);
|
||||
}
|
||||
if (name == "External") {
|
||||
return ColorTheme4f(0.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
if (name == "GpencilMode") {
|
||||
return ColorTheme4f(1.0, 1.0, 0.0, 1.0);
|
||||
}
|
||||
if (name == "UV/Image") {
|
||||
return ColorTheme4f(0.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
if (name == "Overlay") {
|
||||
return ColorTheme4f(0.0, 1.0, 0.5, 1.0);
|
||||
}
|
||||
if (name == "Workbench") {
|
||||
return ColorTheme4f(0.0, 0.7, 1.0, 1.0);
|
||||
}
|
||||
if (name == "Cycles") {
|
||||
return ColorTheme4f(0.0, 0.5, 1.0, 1.0);
|
||||
}
|
||||
if (name == "BackBuffer.Blit") {
|
||||
return ColorTheme4f(0.5, 0.7, 1.0, 1.0);
|
||||
}
|
||||
if (name == "Compositor") {
|
||||
return ColorTheme4f(1.0, 0.5, 0.7, 1.0);
|
||||
}
|
||||
return GPU_DEBUG_GROUP_COLOR_DEFAULT;
|
||||
}
|
||||
} // namespace debug
|
||||
} // namespace blender::gpu
|
||||
551
blender-5.2.0/source/blender/gpu/intern/gpu_batch.cc
Normal file
551
blender-5.2.0/source/blender/gpu/intern/gpu_batch.cc
Normal file
@@ -0,0 +1,551 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU geometry batch
|
||||
* Contains VAOs + VBOs + Shader representing a drawable entity.
|
||||
*/
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_batch_presets.hh"
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_debug_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
void GPU_batch_zero(Batch *batch)
|
||||
{
|
||||
std::fill_n(batch->verts, ARRAY_SIZE(batch->verts), nullptr);
|
||||
batch->elem = nullptr;
|
||||
batch->flag = GPUBatchFlag(0);
|
||||
batch->prim_type = GPUPrimType(0);
|
||||
batch->shader = nullptr;
|
||||
batch->procedural_vertices = -1;
|
||||
}
|
||||
|
||||
Batch *GPU_batch_calloc()
|
||||
{
|
||||
Batch *batch = GPUBackend::get()->batch_alloc();
|
||||
GPU_batch_zero(batch);
|
||||
return batch;
|
||||
}
|
||||
|
||||
Batch *GPU_batch_create_ex(GPUPrimType primitive_type,
|
||||
VertBuf *vertex_buf,
|
||||
IndexBuf *index_buf,
|
||||
GPUBatchFlag owns_flag)
|
||||
{
|
||||
Batch *batch = GPU_batch_calloc();
|
||||
GPU_batch_init_ex(batch, primitive_type, vertex_buf, index_buf, owns_flag);
|
||||
return batch;
|
||||
}
|
||||
|
||||
void GPU_batch_init_ex(Batch *batch,
|
||||
GPUPrimType primitive_type,
|
||||
VertBuf *vertex_buf,
|
||||
IndexBuf *index_buf,
|
||||
GPUBatchFlag owns_flag)
|
||||
{
|
||||
/* Do not pass any other flag */
|
||||
BLI_assert((owns_flag & ~(GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX)) == 0);
|
||||
/* Batch needs to be in cleared state. */
|
||||
BLI_assert((batch->flag & GPU_BATCH_INIT) == 0);
|
||||
|
||||
batch->verts[0] = vertex_buf;
|
||||
for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
|
||||
batch->verts[v] = nullptr;
|
||||
}
|
||||
batch->elem = index_buf;
|
||||
batch->prim_type = primitive_type;
|
||||
batch->flag = owns_flag | GPU_BATCH_INIT | GPU_BATCH_DIRTY;
|
||||
batch->shader = nullptr;
|
||||
batch->procedural_vertices = -1;
|
||||
}
|
||||
|
||||
Batch *GPU_batch_create_procedural(GPUPrimType primitive_type, int32_t vertex_count)
|
||||
{
|
||||
BLI_assert(vertex_count >= 0);
|
||||
Batch *batch = GPU_batch_calloc();
|
||||
for (auto &v : batch->verts) {
|
||||
v = nullptr;
|
||||
}
|
||||
batch->elem = nullptr;
|
||||
batch->prim_type = primitive_type;
|
||||
batch->flag = GPU_BATCH_INIT | GPU_BATCH_DIRTY;
|
||||
batch->shader = nullptr;
|
||||
batch->procedural_vertices = vertex_count;
|
||||
return batch;
|
||||
}
|
||||
|
||||
void GPU_batch_copy(Batch *batch_dst, Batch *batch_src)
|
||||
{
|
||||
GPU_batch_clear(batch_dst);
|
||||
GPU_batch_init_ex(
|
||||
batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, GPU_BATCH_INVALID);
|
||||
|
||||
batch_dst->prim_type = batch_src->prim_type;
|
||||
for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) {
|
||||
batch_dst->verts[v] = batch_src->verts[v];
|
||||
}
|
||||
batch_dst->procedural_vertices = batch_src->procedural_vertices;
|
||||
}
|
||||
|
||||
void GPU_batch_clear(Batch *batch)
|
||||
{
|
||||
if (batch->flag & GPU_BATCH_OWNS_INDEX) {
|
||||
GPU_indexbuf_discard(batch->elem);
|
||||
}
|
||||
if (batch->flag & GPU_BATCH_OWNS_VBO_ANY) {
|
||||
for (int v = 0; (v < GPU_BATCH_VBO_MAX_LEN) && batch->verts[v]; v++) {
|
||||
if (batch->flag & (GPU_BATCH_OWNS_VBO << v)) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(batch->verts[v]);
|
||||
}
|
||||
}
|
||||
}
|
||||
batch->flag = GPU_BATCH_INVALID;
|
||||
batch->procedural_vertices = -1;
|
||||
}
|
||||
|
||||
void GPU_batch_discard(Batch *batch)
|
||||
{
|
||||
GPU_batch_clear(batch);
|
||||
delete batch;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Buffers Management
|
||||
* \{ */
|
||||
|
||||
void GPU_batch_elembuf_set(Batch *batch, gpu::IndexBuf *index_buf, bool own_ibo)
|
||||
{
|
||||
BLI_assert(index_buf);
|
||||
batch->flag |= GPU_BATCH_DIRTY;
|
||||
|
||||
if (batch->elem && (batch->flag & GPU_BATCH_OWNS_INDEX)) {
|
||||
GPU_indexbuf_discard(batch->elem);
|
||||
}
|
||||
batch->elem = index_buf;
|
||||
|
||||
SET_FLAG_FROM_TEST(batch->flag, own_ibo, GPU_BATCH_OWNS_INDEX);
|
||||
}
|
||||
|
||||
int GPU_batch_vertbuf_add(Batch *batch, VertBuf *vertex_buf, bool own_vbo)
|
||||
{
|
||||
BLI_assert(vertex_buf);
|
||||
batch->flag |= GPU_BATCH_DIRTY;
|
||||
|
||||
for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
|
||||
if (batch->verts[v] == nullptr) {
|
||||
/* for now all VertexBuffers must have same vertex_len */
|
||||
if (batch->verts[0] != nullptr) {
|
||||
/* This is an issue for the HACK inside DRW_vbo_request(). */
|
||||
// BLI_assert(verts->vertex_len == batch->verts[0]->vertex_len);
|
||||
}
|
||||
batch->verts[v] = vertex_buf;
|
||||
SET_FLAG_FROM_TEST(batch->flag, own_vbo, (GPUBatchFlag)(GPU_BATCH_OWNS_VBO << v));
|
||||
return v;
|
||||
}
|
||||
}
|
||||
/* we only make it this far if there is no room for another VertBuf */
|
||||
BLI_assert_msg(0, "Not enough VBO slot in batch");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool GPU_batch_vertbuf_has(const Batch *batch, const VertBuf *vertex_buf)
|
||||
{
|
||||
for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
|
||||
if (batch->verts[v] == vertex_buf) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Uniform setters
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void GPU_batch_set_shader(Batch *batch,
|
||||
gpu::Shader *shader,
|
||||
const shader::SpecializationConstants *constants_state)
|
||||
{
|
||||
batch->shader = shader;
|
||||
GPU_shader_bind(batch->shader, constants_state);
|
||||
}
|
||||
|
||||
static uint16_t bind_attribute_as_ssbo(const ShaderInterface *interface,
|
||||
gpu::Shader *shader,
|
||||
VertBuf *vbo)
|
||||
{
|
||||
const GPUVertFormat *format = &vbo->format;
|
||||
|
||||
/* We need to support GPU OpenSubdiv meshes. This assert can be enabled back after we refactor
|
||||
* our OpenSubdiv implementation to output the same layout as the regular mesh extraction. */
|
||||
// BLI_assert_msg(format->attr_len == 1, "Multi attribute buffers are not supported for now");
|
||||
|
||||
char uniform_name[] = "gpu_attr_0";
|
||||
uint stride = format->stride;
|
||||
uint offset = 0;
|
||||
uint16_t bound_attr = 0u;
|
||||
for (uint a_idx = 0; a_idx < format->attr_len; a_idx++) {
|
||||
const GPUVertAttr *a = &format->attrs[a_idx];
|
||||
|
||||
if (format->deinterleaved) {
|
||||
offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].type.size()) * vbo->vertex_len;
|
||||
stride = a->type.size();
|
||||
}
|
||||
else {
|
||||
offset = a->offset;
|
||||
}
|
||||
|
||||
for (uint n_idx = 0; n_idx < a->name_len; n_idx++) {
|
||||
const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
|
||||
const ShaderInput *input = interface->ssbo_get(name);
|
||||
if (input == nullptr || input->location == -1) {
|
||||
continue;
|
||||
}
|
||||
GPU_vertbuf_bind_as_ssbo(vbo, input->location);
|
||||
bound_attr |= (1 << input->location);
|
||||
|
||||
/* WORKAROUND: This is to support complex format. But ideally this should not be supported.
|
||||
*/
|
||||
uniform_name[9] = '0' + input->location;
|
||||
|
||||
/* Only support 4byte aligned attributes. */
|
||||
BLI_assert((stride % 4) == 0);
|
||||
BLI_assert((offset % 4) == 0);
|
||||
int descriptor[2] = {int(stride) / 4, int(offset) / 4};
|
||||
GPU_shader_uniform_2iv(shader, uniform_name, descriptor);
|
||||
/* WORKAROUND: Fix for polyline workaround. Ideally should be fused with `gpu_attr_0`.
|
||||
* But for now, changes are a bit too invasive. Will need to be revisited later on. */
|
||||
char uniform_name_len[] = "gpu_attr_0_len";
|
||||
uniform_name_len[9] = '0' + input->location;
|
||||
const int loc = GPU_shader_get_uniform(shader, uniform_name_len);
|
||||
if (loc != -1) {
|
||||
int data = a->type.comp_len();
|
||||
GPU_shader_uniform_int_ex(shader, loc, 1, 1, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bound_attr;
|
||||
}
|
||||
|
||||
void GPU_batch_bind_as_resources(Batch *batch,
|
||||
gpu::Shader *shader,
|
||||
const shader::SpecializationConstants *constants)
|
||||
{
|
||||
const ShaderInterface *interface = shader->interface;
|
||||
if (interface->ssbo_attr_mask_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t ssbo_attributes = interface->ssbo_attr_mask_;
|
||||
|
||||
if (ssbo_attributes & (1 << GPU_SSBO_INDEX_BUF_SLOT)) {
|
||||
/* Ensure binding for setting uniforms. This is required by the OpenGL backend. */
|
||||
GPU_shader_bind(shader, constants);
|
||||
if (batch->elem) {
|
||||
GPU_indexbuf_bind_as_ssbo(batch->elem, GPU_SSBO_INDEX_BUF_SLOT);
|
||||
GPU_shader_uniform_1b(shader, "gpu_index_no_buffer", false);
|
||||
GPU_shader_uniform_1b(shader, "gpu_index_16bit", !batch->elem->is_32bit());
|
||||
GPU_shader_uniform_1i(shader, "gpu_index_base_index", batch->elem->index_base_get());
|
||||
}
|
||||
else {
|
||||
/* Still fulfill the binding requirements even if the buffer will not be read. */
|
||||
GPU_vertbuf_bind_as_ssbo(batch->verts[0], GPU_SSBO_INDEX_BUF_SLOT);
|
||||
GPU_shader_uniform_1b(shader, "gpu_index_no_buffer", true);
|
||||
}
|
||||
ssbo_attributes &= ~(1 << GPU_SSBO_INDEX_BUF_SLOT);
|
||||
}
|
||||
|
||||
/* Reverse order so first VBO'S have more prevalence (in term of attribute override). */
|
||||
for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
|
||||
VertBuf *vbo = batch->verts_(v);
|
||||
if (vbo) {
|
||||
ssbo_attributes &= ~bind_attribute_as_ssbo(interface, shader, vbo);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_msg(ssbo_attributes == 0, "Not all attribute storage buffer fulfilled");
|
||||
UNUSED_VARS_NDEBUG(ssbo_attributes);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Drawing / Draw-call functions
|
||||
* \{ */
|
||||
|
||||
void GPU_batch_draw_parameter_get(Batch *batch,
|
||||
int *r_vertex_count,
|
||||
int *r_vertex_first,
|
||||
int *r_base_index,
|
||||
int *r_instance_count)
|
||||
{
|
||||
if (batch->procedural_vertices >= 0) {
|
||||
*r_vertex_count = batch->procedural_vertices;
|
||||
*r_vertex_first = 0;
|
||||
*r_base_index = -1;
|
||||
}
|
||||
else if (batch->elem) {
|
||||
*r_vertex_count = batch->elem_()->index_len_get();
|
||||
*r_vertex_first = batch->elem_()->index_start_get();
|
||||
*r_base_index = batch->elem_()->index_base_get();
|
||||
}
|
||||
else {
|
||||
*r_vertex_count = batch->verts_(0)->vertex_len;
|
||||
*r_vertex_first = 0;
|
||||
*r_base_index = -1;
|
||||
}
|
||||
|
||||
*r_instance_count = 1;
|
||||
}
|
||||
|
||||
IndexRange GPU_batch_draw_expanded_parameter_get(GPUPrimType input_prim_type,
|
||||
GPUPrimType output_prim_type,
|
||||
int vertex_count,
|
||||
int vertex_first,
|
||||
int output_primitive_cout)
|
||||
{
|
||||
int vert_per_original_primitive = indices_per_primitive(input_prim_type);
|
||||
int vert_per_expanded_primitive = indices_per_primitive(output_prim_type);
|
||||
|
||||
int prim_first = vertex_first / vert_per_original_primitive;
|
||||
int prim_len = vertex_count / vert_per_original_primitive;
|
||||
|
||||
BLI_assert_msg(vert_per_original_primitive != -1,
|
||||
"Primitive expansion only works for primitives with known amount of vertices");
|
||||
|
||||
/* WORKAROUND: Needed for polyline_draw_workaround. */
|
||||
if (input_prim_type == GPU_PRIM_LINE_STRIP) {
|
||||
prim_len = vertex_count - 1;
|
||||
}
|
||||
|
||||
int out_vertex_first = prim_first * vert_per_expanded_primitive * output_primitive_cout;
|
||||
int out_vertex_count = prim_len * vert_per_expanded_primitive * output_primitive_cout;
|
||||
|
||||
return IndexRange(out_vertex_first, out_vertex_count);
|
||||
}
|
||||
|
||||
static void polyline_draw_workaround(
|
||||
Batch *batch, int vertex_first, int vertex_count, int instance_first, int instance_count)
|
||||
{
|
||||
/* Early out as this can cause crashes on some backend (see #136831). */
|
||||
if (vertex_count == 0) {
|
||||
return;
|
||||
}
|
||||
/* Check compatible input primitive. */
|
||||
BLI_assert(ELEM(batch->prim_type, GPU_PRIM_LINES, GPU_PRIM_LINE_STRIP, GPU_PRIM_LINE_LOOP));
|
||||
|
||||
GPU_batch_bind_as_resources(batch, batch->shader);
|
||||
IndexRange range = GPU_batch_draw_expanded_parameter_get(
|
||||
batch->prim_type, GPU_PRIM_TRIS, vertex_count, vertex_first, 2);
|
||||
Batch *tri_batch = Context::get()->procedural_triangles_batch_get();
|
||||
GPU_batch_set_shader(tri_batch, batch->shader);
|
||||
|
||||
int vert_stride_count[3] = {(batch->prim_type == GPU_PRIM_LINES) ? 2 : 1, vertex_count, 0};
|
||||
GPU_shader_uniform_3iv(batch->shader, "gpu_vert_stride_count_offset", vert_stride_count);
|
||||
/* Assume GPU_FETCH_FLOAT for now. A bit cumbersome to assert for this or to find the correct
|
||||
* attribute. */
|
||||
GPU_shader_uniform_1b(batch->shader, "gpu_attr_0_fetch_int", false);
|
||||
|
||||
/* Allow byte color fetch. */
|
||||
const GPUVertFormat *format = GPU_vertbuf_get_format(batch->verts[0]);
|
||||
int id = GPU_vertformat_attr_id_get(format, "color");
|
||||
if (id != -1) {
|
||||
const GPUVertAttr &attr = format->attrs[id];
|
||||
const bool is_unorm8 = attr.type.format == gpu::VertAttrType::UNORM_8_8_8_8;
|
||||
BLI_assert_msg(is_unorm8 || attr.type.fetch_mode() == GPU_FETCH_FLOAT,
|
||||
"color attribute for polylines can only use GPU_FETCH_INT_TO_FLOAT_UNIT or "
|
||||
"GPU_FETCH_FLOAT");
|
||||
GPU_shader_uniform_1b(batch->shader, "gpu_attr_1_fetch_unorm8", is_unorm8);
|
||||
}
|
||||
|
||||
GPU_batch_draw_advanced(tri_batch, range.start(), range.size(), instance_first, instance_count);
|
||||
}
|
||||
|
||||
void GPU_batch_draw(Batch *batch)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
GPU_shader_bind(batch->shader);
|
||||
if (batch->shader->is_polyline) {
|
||||
polyline_draw_workaround(batch, 0, batch->vertex_count_get(), 0, 0);
|
||||
}
|
||||
else {
|
||||
GPU_batch_draw_advanced(batch, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_batch_draw_range(Batch *batch, int vertex_first, int vertex_count)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
GPU_shader_bind(batch->shader);
|
||||
if (batch->shader->is_polyline) {
|
||||
polyline_draw_workaround(batch, vertex_first, vertex_count, 0, 0);
|
||||
}
|
||||
else {
|
||||
GPU_batch_draw_advanced(batch, vertex_first, vertex_count, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_batch_draw_instance_range(Batch *batch, int instance_first, int instance_count)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
/* Not polyline shaders support instancing. */
|
||||
BLI_assert(batch->shader->is_polyline == false);
|
||||
|
||||
GPU_shader_bind(batch->shader);
|
||||
GPU_batch_draw_advanced(batch, 0, 0, instance_first, instance_count);
|
||||
}
|
||||
|
||||
void GPU_batch_draw_advanced(
|
||||
Batch *batch, int vertex_first, int vertex_count, int instance_first, int instance_count)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
BLI_assert(Context::get()->shader != nullptr);
|
||||
Context::get()->assert_framebuffer_shader_compatibility(Context::get()->shader);
|
||||
|
||||
if (vertex_count == 0) {
|
||||
if (batch->procedural_vertices > 0) {
|
||||
vertex_count = batch->procedural_vertices;
|
||||
}
|
||||
else if (batch->elem) {
|
||||
vertex_count = batch->elem_()->index_len_get();
|
||||
}
|
||||
else {
|
||||
vertex_count = batch->verts_(0)->vertex_len;
|
||||
}
|
||||
}
|
||||
if (instance_count == 0) {
|
||||
instance_count = 1;
|
||||
}
|
||||
|
||||
if (vertex_count == 0 || instance_count == 0) {
|
||||
/* Nothing to draw. */
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
debug_validate_binding_image_format();
|
||||
#endif
|
||||
|
||||
batch->draw(vertex_first, vertex_count, instance_first, instance_count);
|
||||
}
|
||||
|
||||
void GPU_batch_draw_indirect(Batch *batch, gpu::StorageBuf *indirect_buf, intptr_t offset)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
BLI_assert(indirect_buf != nullptr);
|
||||
BLI_assert(Context::get()->shader != nullptr);
|
||||
Context::get()->assert_framebuffer_shader_compatibility(Context::get()->shader);
|
||||
|
||||
#ifndef NDEBUG
|
||||
debug_validate_binding_image_format();
|
||||
#endif
|
||||
|
||||
batch->draw_indirect(indirect_buf, offset);
|
||||
}
|
||||
|
||||
void GPU_batch_multi_draw_indirect(
|
||||
Batch *batch, gpu::StorageBuf *indirect_buf, int count, intptr_t offset, intptr_t stride)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
BLI_assert(indirect_buf != nullptr);
|
||||
BLI_assert(Context::get()->shader != nullptr);
|
||||
Context::get()->assert_framebuffer_shader_compatibility(Context::get()->shader);
|
||||
|
||||
#ifndef NDEBUG
|
||||
debug_validate_binding_image_format();
|
||||
#endif
|
||||
|
||||
batch->multi_draw_indirect(indirect_buf, count, offset, stride);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
|
||||
void GPU_batch_program_set_builtin_with_config(Batch *batch,
|
||||
GPUBuiltinShader shader_id,
|
||||
GPUShaderConfig sh_cfg)
|
||||
{
|
||||
gpu::Shader *shader = GPU_shader_get_builtin_shader_with_config(shader_id, sh_cfg);
|
||||
GPU_batch_set_shader(batch, shader);
|
||||
}
|
||||
|
||||
void GPU_batch_program_set_builtin(Batch *batch, GPUBuiltinShader shader_id)
|
||||
{
|
||||
GPU_batch_program_set_builtin_with_config(batch, shader_id, GPU_SHADER_CFG_DEFAULT);
|
||||
}
|
||||
|
||||
void GPU_batch_program_set_imm_shader(Batch *batch)
|
||||
{
|
||||
GPU_batch_set_shader(batch, immGetShader());
|
||||
}
|
||||
|
||||
gpu::Batch *GPU_batch_procedural_points_get()
|
||||
{
|
||||
return gpu::Context::get()->procedural_points_batch_get();
|
||||
}
|
||||
|
||||
gpu::Batch *GPU_batch_procedural_lines_get()
|
||||
{
|
||||
return gpu::Context::get()->procedural_lines_batch_get();
|
||||
}
|
||||
|
||||
gpu::Batch *GPU_batch_procedural_triangles_get()
|
||||
{
|
||||
return gpu::Context::get()->procedural_triangles_batch_get();
|
||||
}
|
||||
|
||||
gpu::Batch *GPU_batch_procedural_triangle_strips_get()
|
||||
{
|
||||
return gpu::Context::get()->procedural_triangle_strips_batch_get();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Init/Exit
|
||||
* \{ */
|
||||
|
||||
void gpu_batch_init()
|
||||
{
|
||||
gpu_batch_presets_init();
|
||||
}
|
||||
|
||||
void gpu_batch_exit()
|
||||
{
|
||||
gpu_batch_presets_exit();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
289
blender-5.2.0/source/blender/gpu/intern/gpu_batch_presets.cc
Normal file
289
blender-5.2.0/source/blender/gpu/intern/gpu_batch_presets.cc
Normal file
@@ -0,0 +1,289 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_batch_presets.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Local Structures
|
||||
* \{ */
|
||||
|
||||
/* Struct to store 3D Batches and their format */
|
||||
static struct {
|
||||
struct {
|
||||
gpu::Batch *sphere_high;
|
||||
gpu::Batch *sphere_med;
|
||||
gpu::Batch *sphere_low;
|
||||
gpu::Batch *sphere_wire_low;
|
||||
gpu::Batch *sphere_wire_med;
|
||||
} batch;
|
||||
|
||||
GPUVertFormat format;
|
||||
|
||||
struct {
|
||||
uint pos, nor;
|
||||
} attr_id;
|
||||
|
||||
ThreadMutex mutex;
|
||||
} g_presets_3d = {{nullptr}};
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
gpu::Batch *quad;
|
||||
} batch;
|
||||
|
||||
GPUVertFormat format;
|
||||
|
||||
struct {
|
||||
uint pos, col;
|
||||
} attr_id;
|
||||
} g_presets_2d = {{nullptr}};
|
||||
|
||||
static ListBaseT<LinkData> presets_list = {nullptr, nullptr};
|
||||
static ListBaseT<LinkData> buffer_list = {nullptr, nullptr};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name 3D Primitives
|
||||
* \{ */
|
||||
|
||||
static GPUVertFormat &preset_3d_format()
|
||||
{
|
||||
if (g_presets_3d.format.attr_len == 0) {
|
||||
GPUVertFormat *format = &g_presets_3d.format;
|
||||
g_presets_3d.attr_id.pos = GPU_vertformat_attr_add(
|
||||
format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
g_presets_3d.attr_id.nor = GPU_vertformat_attr_add(
|
||||
format, "nor", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
}
|
||||
return g_presets_3d.format;
|
||||
}
|
||||
|
||||
static GPUVertFormat &preset_2d_format()
|
||||
{
|
||||
if (g_presets_2d.format.attr_len == 0) {
|
||||
GPUVertFormat *format = &g_presets_2d.format;
|
||||
g_presets_2d.attr_id.pos = GPU_vertformat_attr_add(
|
||||
format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
g_presets_2d.attr_id.col = GPU_vertformat_attr_add(
|
||||
format, "color", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
}
|
||||
return g_presets_2d.format;
|
||||
}
|
||||
|
||||
static void batch_sphere_lat_lon_vert(GPUVertBufRaw *pos_step,
|
||||
GPUVertBufRaw *nor_step,
|
||||
float lat,
|
||||
float lon)
|
||||
{
|
||||
float pos[3];
|
||||
pos[0] = sinf(lat) * cosf(lon);
|
||||
pos[1] = cosf(lat);
|
||||
pos[2] = sinf(lat) * sinf(lon);
|
||||
copy_v3_v3(static_cast<float *>(GPU_vertbuf_raw_step(pos_step)), pos);
|
||||
copy_v3_v3(static_cast<float *>(GPU_vertbuf_raw_step(nor_step)), pos);
|
||||
}
|
||||
gpu::Batch *GPU_batch_preset_sphere(int lod)
|
||||
{
|
||||
BLI_assert(lod >= 0 && lod <= 2);
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
if (lod == 0) {
|
||||
return g_presets_3d.batch.sphere_low;
|
||||
}
|
||||
if (lod == 1) {
|
||||
return g_presets_3d.batch.sphere_med;
|
||||
}
|
||||
|
||||
return g_presets_3d.batch.sphere_high;
|
||||
}
|
||||
|
||||
gpu::Batch *GPU_batch_preset_sphere_wire(int lod)
|
||||
{
|
||||
BLI_assert(lod >= 0 && lod <= 1);
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
if (lod == 0) {
|
||||
return g_presets_3d.batch.sphere_wire_low;
|
||||
}
|
||||
|
||||
return g_presets_3d.batch.sphere_wire_med;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Create Sphere (3D)
|
||||
* \{ */
|
||||
|
||||
static gpu::Batch *gpu_batch_sphere(int lat_res, int lon_res)
|
||||
{
|
||||
const float lon_inc = 2 * M_PI / lon_res;
|
||||
const float lat_inc = M_PI / lat_res;
|
||||
float lon, lat;
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(preset_3d_format());
|
||||
const uint vbo_len = (lat_res - 1) * lon_res * 6;
|
||||
GPU_vertbuf_data_alloc(*vbo, vbo_len);
|
||||
|
||||
GPUVertBufRaw pos_step, nor_step;
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.pos, &pos_step);
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.nor, &nor_step);
|
||||
|
||||
lon = 0.0f;
|
||||
for (int i = 0; i < lon_res; i++, lon += lon_inc) {
|
||||
lat = 0.0f;
|
||||
for (int j = 0; j < lat_res; j++, lat += lat_inc) {
|
||||
if (j != lat_res - 1) { /* Pole */
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon + lon_inc);
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon);
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon);
|
||||
}
|
||||
|
||||
if (j != 0) { /* Pole */
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon + lon_inc);
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon + lon_inc);
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(vbo_len == GPU_vertbuf_raw_used(&pos_step));
|
||||
BLI_assert(vbo_len == GPU_vertbuf_raw_used(&nor_step));
|
||||
|
||||
return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
static gpu::Batch *batch_sphere_wire(int lat_res, int lon_res)
|
||||
{
|
||||
const float lon_inc = 2 * M_PI / lon_res;
|
||||
const float lat_inc = M_PI / lat_res;
|
||||
float lon, lat;
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(preset_3d_format());
|
||||
const uint vbo_len = (lat_res * lon_res * 2) + ((lat_res - 1) * lon_res * 2);
|
||||
GPU_vertbuf_data_alloc(*vbo, vbo_len);
|
||||
|
||||
GPUVertBufRaw pos_step, nor_step;
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.pos, &pos_step);
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, g_presets_3d.attr_id.nor, &nor_step);
|
||||
|
||||
lon = 0.0f;
|
||||
for (int i = 0; i < lon_res; i++, lon += lon_inc) {
|
||||
lat = 0.0f;
|
||||
for (int j = 0; j < lat_res; j++, lat += lat_inc) {
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon);
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat, lon);
|
||||
|
||||
if (j != lat_res - 1) { /* Pole */
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon + lon_inc);
|
||||
batch_sphere_lat_lon_vert(&pos_step, &nor_step, lat + lat_inc, lon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(vbo_len == GPU_vertbuf_raw_used(&pos_step));
|
||||
BLI_assert(vbo_len == GPU_vertbuf_raw_used(&nor_step));
|
||||
|
||||
return GPU_batch_create_ex(GPU_PRIM_LINES, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
gpu::Batch *GPU_batch_preset_quad()
|
||||
{
|
||||
if (!g_presets_2d.batch.quad) {
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(preset_2d_format());
|
||||
GPU_vertbuf_data_alloc(*vbo, 4);
|
||||
|
||||
float pos_data[4][2] = {{0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}};
|
||||
GPU_vertbuf_attr_fill(vbo, g_presets_2d.attr_id.pos, pos_data);
|
||||
/* Don't fill the color. */
|
||||
|
||||
g_presets_2d.batch.quad = GPU_batch_create_ex(
|
||||
GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
|
||||
gpu_batch_presets_register(g_presets_2d.batch.quad);
|
||||
}
|
||||
return g_presets_2d.batch.quad;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Preset Registration Management
|
||||
* \{ */
|
||||
|
||||
void gpu_batch_presets_init()
|
||||
{
|
||||
BLI_mutex_init(&g_presets_3d.mutex);
|
||||
|
||||
/* Hard coded resolution */
|
||||
g_presets_3d.batch.sphere_low = gpu_batch_sphere(8, 16);
|
||||
gpu_batch_presets_register(g_presets_3d.batch.sphere_low);
|
||||
|
||||
g_presets_3d.batch.sphere_med = gpu_batch_sphere(16, 10);
|
||||
gpu_batch_presets_register(g_presets_3d.batch.sphere_med);
|
||||
|
||||
g_presets_3d.batch.sphere_high = gpu_batch_sphere(32, 24);
|
||||
gpu_batch_presets_register(g_presets_3d.batch.sphere_high);
|
||||
|
||||
g_presets_3d.batch.sphere_wire_low = batch_sphere_wire(6, 8);
|
||||
gpu_batch_presets_register(g_presets_3d.batch.sphere_wire_low);
|
||||
|
||||
g_presets_3d.batch.sphere_wire_med = batch_sphere_wire(8, 16);
|
||||
gpu_batch_presets_register(g_presets_3d.batch.sphere_wire_med);
|
||||
}
|
||||
|
||||
void gpu_batch_presets_register(gpu::Batch *preset_batch)
|
||||
{
|
||||
BLI_mutex_lock(&g_presets_3d.mutex);
|
||||
BLI_addtail(&presets_list, BLI_genericNodeN(preset_batch));
|
||||
BLI_mutex_unlock(&g_presets_3d.mutex);
|
||||
}
|
||||
|
||||
void gpu_batch_storage_buffer_register(gpu::StorageBuf *preset_buffer)
|
||||
{
|
||||
BLI_mutex_lock(&g_presets_3d.mutex);
|
||||
BLI_addtail(&buffer_list, BLI_genericNodeN(preset_buffer));
|
||||
BLI_mutex_unlock(&g_presets_3d.mutex);
|
||||
}
|
||||
|
||||
void gpu_batch_presets_exit()
|
||||
{
|
||||
while (LinkData *link = static_cast<LinkData *>(BLI_pophead(&presets_list))) {
|
||||
gpu::Batch *preset = static_cast<gpu::Batch *>(link->data);
|
||||
GPU_batch_discard(preset);
|
||||
MEM_delete(link);
|
||||
}
|
||||
|
||||
while (LinkData *link = static_cast<LinkData *>(BLI_pophead(&buffer_list))) {
|
||||
gpu::StorageBuf *preset = static_cast<gpu::StorageBuf *>(link->data);
|
||||
GPU_storagebuf_free(preset);
|
||||
MEM_delete(link);
|
||||
}
|
||||
|
||||
/* Reset pointers to null for subsequent initializations after tear-down. */
|
||||
g_presets_2d = {{nullptr}};
|
||||
g_presets_3d = {{nullptr}};
|
||||
presets_list = {nullptr, nullptr};
|
||||
|
||||
BLI_mutex_end(&g_presets_3d.mutex);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
285
blender-5.2.0/source/blender/gpu/intern/gpu_batch_utils.cc
Normal file
285
blender-5.2.0/source/blender/gpu/intern/gpu_batch_utils.cc
Normal file
@@ -0,0 +1,285 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_polyfill_2d.h"
|
||||
#include "BLI_sort_utils.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_batch_utils.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Polygon Creation (2D)
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *GPU_batch_tris_from_poly_2d_encoded(const uchar *polys_flat,
|
||||
uint polys_flat_len,
|
||||
const rctf *rect)
|
||||
{
|
||||
const uchar(*polys)[2] = reinterpret_cast<const uchar(*)[2]>(polys_flat);
|
||||
const uint polys_len = polys_flat_len / 2;
|
||||
BLI_assert(polys_flat_len == polys_len * 2);
|
||||
|
||||
/* Over alloc in both cases */
|
||||
float (*verts)[2] = MEM_new_array_uninitialized<float[2]>(polys_len, __func__);
|
||||
float (*verts_step)[2] = verts;
|
||||
uint(*tris)[3] = MEM_new_array_uninitialized<uint[3]>(polys_len, __func__);
|
||||
uint(*tris_step)[3] = tris;
|
||||
|
||||
const float range_uchar[2] = {
|
||||
(rect ? (rect->xmax - rect->xmin) : 2.0f) / 255.0f,
|
||||
(rect ? (rect->ymax - rect->ymin) : 2.0f) / 255.0f,
|
||||
};
|
||||
const float min_uchar[2] = {
|
||||
(rect ? rect->xmin : -1.0f),
|
||||
(rect ? rect->ymin : -1.0f),
|
||||
};
|
||||
|
||||
uint i_poly = 0;
|
||||
uint i_vert = 0;
|
||||
while (i_poly != polys_len) {
|
||||
for (uint j = 0; j < 2; j++) {
|
||||
verts[i_vert][j] = min_uchar[j] + (float(polys[i_poly][j]) * range_uchar[j]);
|
||||
}
|
||||
i_vert++;
|
||||
i_poly++;
|
||||
if (polys[i_poly - 1][0] == polys[i_poly][0] && polys[i_poly - 1][1] == polys[i_poly][1]) {
|
||||
const uint verts_step_len = (&verts[i_vert]) - verts_step;
|
||||
BLI_assert(verts_step_len >= 3);
|
||||
const uint tris_len = (verts_step_len - 2);
|
||||
BLI_polyfill_calc(verts_step, verts_step_len, -1, tris_step);
|
||||
/* offset indices */
|
||||
if (verts_step != verts) {
|
||||
uint *t = tris_step[0];
|
||||
const uint offset = (verts_step - verts);
|
||||
uint tot = tris_len * 3;
|
||||
while (tot--) {
|
||||
*t += offset;
|
||||
t++;
|
||||
}
|
||||
BLI_assert(t == tris_step[tris_len]);
|
||||
}
|
||||
verts_step += verts_step_len;
|
||||
tris_step += tris_len;
|
||||
i_poly++;
|
||||
/* ignore the duplicate point */
|
||||
}
|
||||
}
|
||||
|
||||
/* We have vertices and tris, make a batch from this. */
|
||||
static GPUVertFormat format = {0};
|
||||
static struct {
|
||||
uint pos;
|
||||
} attr_id;
|
||||
if (format.attr_len == 0) {
|
||||
attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
}
|
||||
|
||||
const uint verts_len = (verts_step - verts);
|
||||
const uint tris_len = (tris_step - tris);
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*vbo, verts_len);
|
||||
|
||||
GPUVertBufRaw pos_step;
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, attr_id.pos, &pos_step);
|
||||
|
||||
for (uint i = 0; i < verts_len; i++) {
|
||||
copy_v2_v2(static_cast<float *>(GPU_vertbuf_raw_step(&pos_step)), verts[i]);
|
||||
}
|
||||
|
||||
GPUIndexBufBuilder elb;
|
||||
GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tris_len, verts_len);
|
||||
for (uint i = 0; i < tris_len; i++) {
|
||||
GPU_indexbuf_add_tri_verts(&elb, UNPACK3(tris[i]));
|
||||
}
|
||||
gpu::IndexBuf *indexbuf = GPU_indexbuf_build(&elb);
|
||||
|
||||
MEM_delete(tris);
|
||||
MEM_delete(verts);
|
||||
|
||||
return GPU_batch_create_ex(
|
||||
GPU_PRIM_TRIS, vbo, indexbuf, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
|
||||
}
|
||||
|
||||
gpu::Batch *GPU_batch_wire_from_poly_2d_encoded(const uchar *polys_flat,
|
||||
uint polys_flat_len,
|
||||
const rctf *rect)
|
||||
{
|
||||
const uchar(*polys)[2] = reinterpret_cast<const uchar(*)[2]>(polys_flat);
|
||||
const uint polys_len = polys_flat_len / 2;
|
||||
BLI_assert(polys_flat_len == polys_len * 2);
|
||||
|
||||
/* Over alloc */
|
||||
/* Lines are pairs of (x, y) byte locations packed into an int32_t. */
|
||||
int32_t *lines = MEM_new_array_uninitialized<int32_t>(polys_len, __func__);
|
||||
int32_t *lines_step = lines;
|
||||
|
||||
const float range_uchar[2] = {
|
||||
(rect ? (rect->xmax - rect->xmin) : 2.0f) / 255.0f,
|
||||
(rect ? (rect->ymax - rect->ymin) : 2.0f) / 255.0f,
|
||||
};
|
||||
const float min_uchar[2] = {
|
||||
(rect ? rect->xmin : -1.0f),
|
||||
(rect ? rect->ymin : -1.0f),
|
||||
};
|
||||
|
||||
uint i_poly_prev = 0;
|
||||
uint i_poly = 0;
|
||||
while (i_poly != polys_len) {
|
||||
i_poly++;
|
||||
if (polys[i_poly - 1][0] == polys[i_poly][0] && polys[i_poly - 1][1] == polys[i_poly][1]) {
|
||||
const uchar(*polys_step)[2] = polys + i_poly_prev;
|
||||
const uint polys_step_len = i_poly - i_poly_prev;
|
||||
BLI_assert(polys_step_len >= 2);
|
||||
for (uint i_prev = polys_step_len - 1, i = 0; i < polys_step_len; i_prev = i++) {
|
||||
union {
|
||||
uint16_t as_u16[2];
|
||||
uint32_t as_u32;
|
||||
} data;
|
||||
data.as_u16[0] = *(reinterpret_cast<const uint16_t *>(polys_step[i_prev]));
|
||||
data.as_u16[1] = *(reinterpret_cast<const uint16_t *>(polys_step[i]));
|
||||
if (data.as_u16[0] > data.as_u16[1]) {
|
||||
std::swap(data.as_u16[0], data.as_u16[1]);
|
||||
}
|
||||
*lines_step = data.as_u32;
|
||||
lines_step++;
|
||||
}
|
||||
i_poly++;
|
||||
i_poly_prev = i_poly;
|
||||
/* ignore the duplicate point */
|
||||
}
|
||||
}
|
||||
|
||||
uint lines_len = lines_step - lines;
|
||||
|
||||
/* Hide Lines (we could make optional) */
|
||||
{
|
||||
qsort(lines, lines_len, sizeof(int32_t), BLI_sortutil_cmp_int);
|
||||
lines_step = lines;
|
||||
for (uint i_prev = 0, i = 1; i < lines_len; i_prev = i++) {
|
||||
if (lines[i] != lines[i_prev]) {
|
||||
*lines_step++ = lines[i_prev];
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
*lines_step++ = lines[lines_len - 1];
|
||||
lines_len = lines_step - lines;
|
||||
}
|
||||
|
||||
/* We have vertices and tris, make a batch from this. */
|
||||
static GPUVertFormat format = {0};
|
||||
static struct {
|
||||
uint pos;
|
||||
} attr_id;
|
||||
if (format.attr_len == 0) {
|
||||
attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
}
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
const uint vbo_len_capacity = lines_len * 2;
|
||||
GPU_vertbuf_data_alloc(*vbo, vbo_len_capacity);
|
||||
|
||||
GPUVertBufRaw pos_step;
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, attr_id.pos, &pos_step);
|
||||
|
||||
for (uint i = 0; i < lines_len; i++) {
|
||||
union {
|
||||
uint8_t as_u8_pair[2][2];
|
||||
uint32_t as_u32;
|
||||
} data;
|
||||
data.as_u32 = lines[i];
|
||||
for (uint k = 0; k < 2; k++) {
|
||||
float *pos_v2 = static_cast<float *>(GPU_vertbuf_raw_step(&pos_step));
|
||||
for (uint j = 0; j < 2; j++) {
|
||||
pos_v2[j] = min_uchar[j] + (float(data.as_u8_pair[k][j]) * range_uchar[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
BLI_assert(vbo_len_capacity == GPU_vertbuf_raw_used(&pos_step));
|
||||
MEM_delete(lines);
|
||||
return GPU_batch_create_ex(GPU_PRIM_LINES, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Common shapes (3D)
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *GPU_batch_unit_cube()
|
||||
{
|
||||
static const std::array<float3, 8> bone_box_verts = {
|
||||
float3{1.0f, -1.0f, 1.0f},
|
||||
float3{1.0f, -1.0f, -1.0f},
|
||||
float3{-1.0f, -1.0f, -1.0f},
|
||||
float3{-1.0f, -1.0f, 1.0f},
|
||||
float3{1.0f, 1.0f, 1.0f},
|
||||
float3{1.0f, 1.0f, -1.0f},
|
||||
float3{-1.0f, 1.0f, -1.0f},
|
||||
float3{-1.0f, 1.0f, 1.0f},
|
||||
};
|
||||
|
||||
static const std::array<int3, 12> bone_box_solid_tris = {
|
||||
int3{0, 2, 1}, /* bottom */
|
||||
int3{0, 3, 2},
|
||||
|
||||
int3{0, 1, 5}, /* sides */
|
||||
int3{0, 5, 4},
|
||||
|
||||
int3{1, 2, 6},
|
||||
int3{1, 6, 5},
|
||||
|
||||
int3{2, 3, 7},
|
||||
int3{2, 7, 6},
|
||||
|
||||
int3{3, 0, 4},
|
||||
int3{3, 4, 7},
|
||||
|
||||
int3{4, 5, 6}, /* top */
|
||||
int3{4, 6, 7},
|
||||
};
|
||||
|
||||
GPUVertFormat format = {0};
|
||||
GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
|
||||
const int tri_len = bone_box_solid_tris.size();
|
||||
const int vert_len = bone_box_verts.size();
|
||||
|
||||
GPU_vertbuf_data_alloc(*vbo, vert_len);
|
||||
|
||||
GPUIndexBufBuilder elb;
|
||||
GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tri_len, vert_len);
|
||||
|
||||
int v = 0;
|
||||
for (int i = 0; i < vert_len; i++) {
|
||||
GPU_vertbuf_vert_set(vbo, v++, &bone_box_verts[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < tri_len; i++) {
|
||||
const int3 tri_indices = bone_box_solid_tris[i];
|
||||
GPU_indexbuf_add_tri_verts(&elb, tri_indices[0], tri_indices[1], tri_indices[2]);
|
||||
}
|
||||
|
||||
return GPU_batch_create_ex(
|
||||
GPU_PRIM_TRIS, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
237
blender-5.2.0/source/blender/gpu/intern/gpu_capabilities.cc
Normal file
237
blender-5.2.0/source/blender/gpu/intern/gpu_capabilities.cc
Normal file
@@ -0,0 +1,237 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Wrap OpenGL features such as textures, shaders and GLSL
|
||||
* with checks for drivers and GPU support.
|
||||
*/
|
||||
|
||||
#include "DNA_userdef_types.h" /* For `U.glreslimit`. */
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "gpu_capabilities_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
|
||||
GPUCapabilities GCaps = {};
|
||||
|
||||
}
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Capabilities
|
||||
* \{ */
|
||||
|
||||
int GPU_max_texture_size()
|
||||
{
|
||||
return GCaps.max_texture_size;
|
||||
}
|
||||
|
||||
int GPU_max_texture_3d_size()
|
||||
{
|
||||
return GCaps.max_texture_3d_size;
|
||||
}
|
||||
|
||||
uint32_t GPU_max_buffer_texture_size()
|
||||
{
|
||||
return GCaps.max_buffer_texture_size;
|
||||
}
|
||||
|
||||
int GPU_texture_size_with_limit(int res)
|
||||
{
|
||||
int size = GPU_max_texture_size();
|
||||
int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size;
|
||||
return min_ii(reslimit, res);
|
||||
}
|
||||
|
||||
bool GPU_is_safe_texture_size(int width, int height)
|
||||
{
|
||||
const int max_texture_size = GPU_max_texture_size();
|
||||
return size_t(width) * height <= size_t(max_texture_size) * max_texture_size / 4;
|
||||
}
|
||||
|
||||
int GPU_max_texture_layers()
|
||||
{
|
||||
return GCaps.max_texture_layers;
|
||||
}
|
||||
|
||||
int GPU_max_textures()
|
||||
{
|
||||
return GCaps.max_textures;
|
||||
}
|
||||
|
||||
int GPU_max_images()
|
||||
{
|
||||
return GCaps.max_images;
|
||||
}
|
||||
|
||||
int GPU_max_work_group_count(int index)
|
||||
{
|
||||
return GCaps.max_work_group_count[index];
|
||||
}
|
||||
|
||||
int GPU_max_work_group_size(int index)
|
||||
{
|
||||
return GCaps.max_work_group_size[index];
|
||||
}
|
||||
|
||||
int GPU_max_uniforms_vert()
|
||||
{
|
||||
return GCaps.max_uniforms_vert;
|
||||
}
|
||||
|
||||
int GPU_max_uniforms_frag()
|
||||
{
|
||||
return GCaps.max_uniforms_frag;
|
||||
}
|
||||
|
||||
int GPU_max_batch_indices()
|
||||
{
|
||||
return GCaps.max_batch_indices;
|
||||
}
|
||||
|
||||
int GPU_max_batch_vertices()
|
||||
{
|
||||
return GCaps.max_batch_vertices;
|
||||
}
|
||||
|
||||
int GPU_max_vertex_attribs()
|
||||
{
|
||||
return GCaps.max_vertex_attribs;
|
||||
}
|
||||
|
||||
int GPU_max_varying_floats()
|
||||
{
|
||||
return GCaps.max_varying_floats;
|
||||
}
|
||||
|
||||
int GPU_extensions_len()
|
||||
{
|
||||
return GCaps.extensions_len;
|
||||
}
|
||||
|
||||
const char *GPU_extension_get(int i)
|
||||
{
|
||||
return GCaps.extension_get ? GCaps.extension_get(i) : "\0";
|
||||
}
|
||||
|
||||
bool GPU_use_subprocess_compilation()
|
||||
{
|
||||
return GCaps.use_subprocess_shader_compilations;
|
||||
}
|
||||
|
||||
int GPU_max_parallel_compilations()
|
||||
{
|
||||
return GCaps.max_parallel_compilations;
|
||||
}
|
||||
|
||||
bool GPU_depth_blitting_workaround()
|
||||
{
|
||||
return GCaps.depth_blitting_workaround;
|
||||
}
|
||||
|
||||
bool GPU_use_main_context_workaround()
|
||||
{
|
||||
return GCaps.use_main_context_workaround;
|
||||
}
|
||||
|
||||
bool GPU_use_hq_normals_workaround()
|
||||
{
|
||||
return GCaps.use_hq_normals_workaround;
|
||||
}
|
||||
|
||||
bool GPU_stencil_clasify_buffer_workaround()
|
||||
{
|
||||
return GCaps.stencil_clasify_buffer_workaround;
|
||||
}
|
||||
|
||||
bool GPU_geometry_shader_support()
|
||||
{
|
||||
return GCaps.geometry_shader_support;
|
||||
}
|
||||
|
||||
bool GPU_hdr_support()
|
||||
{
|
||||
return GCaps.hdr_viewport_support;
|
||||
}
|
||||
|
||||
bool GPU_stencil_export_support()
|
||||
{
|
||||
return GCaps.stencil_export_support;
|
||||
}
|
||||
|
||||
int GPU_max_shader_storage_buffer_bindings()
|
||||
{
|
||||
return GCaps.max_shader_storage_buffer_bindings;
|
||||
}
|
||||
|
||||
int GPU_max_compute_shader_storage_blocks()
|
||||
{
|
||||
return GCaps.max_compute_shader_storage_blocks;
|
||||
}
|
||||
|
||||
int GPU_minimum_per_vertex_stride()
|
||||
{
|
||||
return GCaps.minimum_per_vertex_stride;
|
||||
}
|
||||
|
||||
size_t GPU_max_uniform_buffer_size()
|
||||
{
|
||||
return GCaps.max_uniform_buffer_size;
|
||||
}
|
||||
|
||||
size_t GPU_max_storage_buffer_size()
|
||||
{
|
||||
return GCaps.max_storage_buffer_size;
|
||||
}
|
||||
|
||||
size_t GPU_storage_buffer_alignment()
|
||||
{
|
||||
return GCaps.storage_buffer_alignment;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Memory statistics
|
||||
* \{ */
|
||||
|
||||
bool GPU_mem_stats_supported()
|
||||
{
|
||||
return GCaps.mem_stats_support;
|
||||
}
|
||||
|
||||
void GPU_mem_stats_get(int *r_totalmem, int *r_freemem)
|
||||
{
|
||||
Context::get()->memory_statistics_get(r_totalmem, r_freemem);
|
||||
}
|
||||
|
||||
bool GPU_stereo_quadbuffer_support()
|
||||
{
|
||||
return Context::get()->front_right != nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creator arguments overrides
|
||||
* \{ */
|
||||
|
||||
void GPU_compilation_subprocess_override_set(int count)
|
||||
{
|
||||
BLI_assert(GCaps.max_parallel_compilations == -1);
|
||||
GCaps.max_parallel_compilations = count;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/**
|
||||
* This includes both hardware capabilities & workarounds.
|
||||
* Try to limit these to the implementation code-base (i.e.: `gpu/opengl/`).
|
||||
* Only add workarounds here if they are common to all implementation or
|
||||
* if you need access to it outside of the GPU module.
|
||||
* Same goes for capabilities (i.e.: texture size).
|
||||
*/
|
||||
struct GPUCapabilities {
|
||||
int max_texture_size = 0;
|
||||
int max_texture_3d_size = 0;
|
||||
uint32_t max_buffer_texture_size = 0;
|
||||
int max_texture_layers = 0;
|
||||
int max_textures = 0;
|
||||
int max_images = 0;
|
||||
int max_work_group_count[3] = {0, 0, 0};
|
||||
int max_work_group_size[3] = {0, 0, 0};
|
||||
int max_uniforms_vert = 0;
|
||||
int max_uniforms_frag = 0;
|
||||
int max_batch_indices = 0;
|
||||
int max_batch_vertices = 0;
|
||||
int max_vertex_attribs = 0;
|
||||
int max_varying_floats = 0;
|
||||
int max_shader_storage_buffer_bindings = 0;
|
||||
int max_compute_shader_storage_blocks = 0;
|
||||
size_t max_uniform_buffer_size = 0;
|
||||
size_t max_storage_buffer_size = 0;
|
||||
size_t storage_buffer_alignment = 256;
|
||||
int extensions_len = 0;
|
||||
const char *(*extension_get)(int);
|
||||
|
||||
bool mem_stats_support = false;
|
||||
bool geometry_shader_support = false;
|
||||
bool hdr_viewport_support = false;
|
||||
bool stencil_export_support = false;
|
||||
|
||||
int max_parallel_compilations = -1;
|
||||
|
||||
/* OpenGL related workarounds. */
|
||||
bool depth_blitting_workaround = false;
|
||||
bool use_main_context_workaround = false;
|
||||
bool use_hq_normals_workaround = false;
|
||||
bool stencil_clasify_buffer_workaround = false;
|
||||
bool texture_pool_workaround = false;
|
||||
|
||||
bool use_subprocess_shader_compilations = false;
|
||||
|
||||
/* Metal related workarounds. */
|
||||
/* Minimum per-vertex stride in bytes (For a vertex buffer). */
|
||||
int minimum_per_vertex_stride = 1;
|
||||
};
|
||||
|
||||
extern GPUCapabilities GCaps;
|
||||
|
||||
} // namespace blender::gpu
|
||||
600
blender-5.2.0/source/blender/gpu/intern/gpu_codegen.cc
Normal file
600
blender-5.2.0/source/blender/gpu/intern/gpu_codegen.cc
Normal file
@@ -0,0 +1,600 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Convert material node-trees to GLSL.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_uniform_buffer.hh"
|
||||
#include "GPU_vertex_format.hh"
|
||||
|
||||
#include "gpu_codegen.hh"
|
||||
#include "gpu_material_library.hh"
|
||||
#include "gpu_shader_dependency_private.hh"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Type > string conversion
|
||||
* \{ */
|
||||
|
||||
static std::ostream &operator<<(std::ostream &stream, const GPUInput *input)
|
||||
{
|
||||
switch (input->source) {
|
||||
case GPU_SOURCE_FUNCTION_CALL:
|
||||
case GPU_SOURCE_OUTPUT:
|
||||
return stream << (input->is_zone_io ? "zone" : "tmp") << input->id;
|
||||
case GPU_SOURCE_CONSTANT:
|
||||
return stream << (input->is_zone_io ? "zone" : "cons") << input->id;
|
||||
case GPU_SOURCE_UNIFORM:
|
||||
return stream << "node_tree.u" << input->id << (input->is_duplicate ? "b" : "");
|
||||
case GPU_SOURCE_ATTR:
|
||||
return stream << "var_attrs.v" << input->attr->id;
|
||||
case GPU_SOURCE_UNIFORM_ATTR:
|
||||
return stream << "UNI_ATTR(unf_attrs[resource_id].attr" << input->uniform_attr->id << ")";
|
||||
case GPU_SOURCE_LAYER_ATTR:
|
||||
return stream << "attr_load_layer(" << input->layer_attr->hash_code << ")";
|
||||
case GPU_SOURCE_STRUCT:
|
||||
return stream << "strct" << input->id;
|
||||
case GPU_SOURCE_TEX:
|
||||
return stream << input->texture->sampler_name;
|
||||
case GPU_SOURCE_TEX_TILED_MAPPING:
|
||||
return stream << input->texture->tiled_mapping_name;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output)
|
||||
{
|
||||
return stream << (output->is_zone_io ? "zone" : "tmp") << output->id;
|
||||
}
|
||||
|
||||
/* Print data constructor (i.e: vec2(1.0f, 1.0f)). */
|
||||
static std::ostream &operator<<(std::ostream &stream, const Span<float> &span)
|
||||
{
|
||||
stream << GPUType(span.size()) << "(";
|
||||
/* Use uint representation to allow exact same bit pattern even if NaN. This is
|
||||
* because we can pass UINTs as floats for constants. */
|
||||
const Span<uint32_t> uint_span = span.cast<uint32_t>();
|
||||
for (const uint32_t &element : uint_span) {
|
||||
char formatted_float[32];
|
||||
SNPRINTF(formatted_float, "uintBitsToFloat(%uu)", element);
|
||||
stream << formatted_float;
|
||||
if (&element != &uint_span.last()) {
|
||||
stream << ", ";
|
||||
}
|
||||
}
|
||||
stream << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
/* Trick type to change overload and keep a somewhat nice syntax. */
|
||||
struct GPUConstant : public GPUInput {};
|
||||
|
||||
static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input)
|
||||
{
|
||||
stream << Span<float>(input->vec, input->type);
|
||||
return stream;
|
||||
}
|
||||
|
||||
namespace gpu::shader {
|
||||
/* Needed to use the << operators from nested namespaces. :(
|
||||
* https://stackoverflow.com/questions/5195512/namespaces-and-operator-resolution */
|
||||
using blender::operator<<;
|
||||
} // namespace gpu::shader
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GLSL code generation
|
||||
* \{ */
|
||||
|
||||
const char *GPUCodegenCreateInfo::NameBuffer::append_sampler_name(const char name[32])
|
||||
{
|
||||
auto index = sampler_names.size();
|
||||
sampler_names.append(std::make_unique<NameEntry>());
|
||||
char *name_buffer = sampler_names[index]->data();
|
||||
memcpy(name_buffer, name, 32);
|
||||
return name_buffer;
|
||||
}
|
||||
|
||||
GPUCodegen::GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_, const char *debug_name)
|
||||
: mat(*mat_), graph(*graph_)
|
||||
{
|
||||
BLI_hash_mm2a_init(&hm2a_, GPU_material_uuid_get(&mat));
|
||||
BLI_hash_mm2a_add_int(&hm2a_, GPU_material_flag(&mat));
|
||||
create_info = MEM_new<GPUCodegenCreateInfo>(__func__, debug_name);
|
||||
output.create_info = reinterpret_cast<GPUShaderCreateInfo *>(
|
||||
static_cast<ShaderCreateInfo *>(create_info));
|
||||
}
|
||||
|
||||
GPUCodegen::~GPUCodegen()
|
||||
{
|
||||
MEM_SAFE_DELETE(cryptomatte_input_);
|
||||
MEM_delete(create_info);
|
||||
ubo_inputs_.free_no_destruct();
|
||||
};
|
||||
|
||||
bool GPUCodegen::should_optimize_heuristic() const
|
||||
{
|
||||
/* If each of the maximal attributes are exceeded, we can optimize, but we should also ensure
|
||||
* the baseline is met. */
|
||||
bool do_optimize = (nodes_total_ >= 60 || textures_total_ >= 4 || uniforms_total_ >= 64) &&
|
||||
(textures_total_ >= 1 && uniforms_total_ >= 8 && nodes_total_ >= 4);
|
||||
return do_optimize;
|
||||
}
|
||||
|
||||
void GPUCodegen::generate_attribs()
|
||||
{
|
||||
if (graph.attributes.is_empty()) {
|
||||
output.attr_load.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
GPUCodegenCreateInfo &info = *create_info;
|
||||
|
||||
info.interface_generated = MEM_new<StageInterfaceInfo>(__func__, "codegen_iface", "var_attrs");
|
||||
StageInterfaceInfo &iface = *info.interface_generated;
|
||||
info.vertex_out(iface);
|
||||
|
||||
/* Input declaration, loading / assignment to interface and geometry shader passthrough. */
|
||||
std::stringstream load_ss;
|
||||
|
||||
/* Index of the attribute as ordered in graph.attributes. */
|
||||
int attr_n = 0;
|
||||
int slot = 15;
|
||||
for (GPUMaterialAttribute &attr : graph.attributes) {
|
||||
if (slot == -1) {
|
||||
BLI_assert_msg(0, "Too many attributes");
|
||||
break;
|
||||
}
|
||||
STRNCPY(info.name_buffer.attr_names[slot], attr.input_name);
|
||||
SNPRINTF(info.name_buffer.var_names[slot], "v%d", attr.id);
|
||||
|
||||
StringRefNull attr_name = info.name_buffer.attr_names[slot];
|
||||
StringRefNull var_name = info.name_buffer.var_names[slot];
|
||||
|
||||
GPUType input_type, iface_type;
|
||||
|
||||
load_ss << "var_attrs." << var_name;
|
||||
if (attr.is_hair_length || attr.is_hair_intercept) {
|
||||
iface_type = input_type = GPU_FLOAT;
|
||||
load_ss << " = attr_load_" << input_type << "(domain, " << attr_name << ", " << attr_n
|
||||
<< ");\n";
|
||||
}
|
||||
else {
|
||||
switch (attr.type) {
|
||||
case CD_ORCO:
|
||||
/* Need vec4 to detect usage of default attribute. */
|
||||
input_type = GPU_VEC4;
|
||||
iface_type = GPU_VEC3;
|
||||
load_ss << " = attr_load_orco(domain, " << attr_name << ", " << attr_n << ");\n";
|
||||
break;
|
||||
case CD_TANGENT:
|
||||
iface_type = input_type = GPU_VEC4;
|
||||
load_ss << " = attr_load_tangent(domain, " << attr_name << ", " << attr_n << ");\n";
|
||||
break;
|
||||
default:
|
||||
iface_type = input_type = GPU_VEC4;
|
||||
load_ss << " = attr_load_" << input_type << "(domain, " << attr_name << ", " << attr_n
|
||||
<< ");\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
attr_n++;
|
||||
|
||||
info.vertex_in(slot--, to_type(input_type), attr_name);
|
||||
iface.smooth(to_type(iface_type), var_name);
|
||||
}
|
||||
|
||||
output.attr_load = load_ss.str();
|
||||
}
|
||||
|
||||
void GPUCodegen::generate_resources()
|
||||
{
|
||||
GPUCodegenCreateInfo &info = *create_info;
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
/* Textures. */
|
||||
int slot = 0;
|
||||
for (GPUMaterialTexture &tex : graph.textures) {
|
||||
if (tex.colorband) {
|
||||
const char *name = info.name_buffer.append_sampler_name(tex.sampler_name);
|
||||
info.sampler(slot++, ImageType::Float1DArray, name, Frequency::BATCH);
|
||||
}
|
||||
else if (tex.sky) {
|
||||
const char *name = info.name_buffer.append_sampler_name(tex.sampler_name);
|
||||
info.sampler(0, ImageType::Float2DArray, name, Frequency::BATCH);
|
||||
}
|
||||
else if (tex.tiled_mapping_name[0] != '\0') {
|
||||
const char *name = info.name_buffer.append_sampler_name(tex.sampler_name);
|
||||
info.sampler(slot++, ImageType::Float2DArray, name, Frequency::BATCH);
|
||||
|
||||
const char *name_mapping = info.name_buffer.append_sampler_name(tex.tiled_mapping_name);
|
||||
info.sampler(slot++, ImageType::Float1DArray, name_mapping, Frequency::BATCH);
|
||||
}
|
||||
else {
|
||||
const char *name = info.name_buffer.append_sampler_name(tex.sampler_name);
|
||||
info.sampler(slot++, ImageType::Float2D, name, Frequency::BATCH);
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment heuristic. */
|
||||
textures_total_ = slot;
|
||||
|
||||
if (!ubo_inputs_.is_empty()) {
|
||||
const char *linted_struct_suffix = "_host_shared_";
|
||||
/* NOTE: generate_uniform_buffer() should have sorted the inputs before this. */
|
||||
ss << "struct NodeTree {\n";
|
||||
for (LinkData &link : ubo_inputs_) {
|
||||
GPUInput *input = static_cast<GPUInput *>(link.data);
|
||||
if (input->source == GPU_SOURCE_CRYPTOMATTE) {
|
||||
ss << input->type << " crypto_hash;\n";
|
||||
}
|
||||
else {
|
||||
ss << input->type << " u" << input->id << (input->is_duplicate ? "b" : "") << ";\n";
|
||||
}
|
||||
}
|
||||
ss << "};\n";
|
||||
ss << "#define NodeTree" << linted_struct_suffix << " NodeTree\n";
|
||||
ss << "#define NodeTree" << linted_struct_suffix << "uniform_ NodeTree\n";
|
||||
ss << "\n";
|
||||
|
||||
info.uniform_buf(GPU_NODE_TREE_UBO_SLOT, "NodeTree", GPU_UBO_BLOCK_NAME, Frequency::BATCH);
|
||||
}
|
||||
|
||||
if (!graph.uniform_attrs.list.is_empty()) {
|
||||
ss << "struct UniformAttrs {\n";
|
||||
for (GPUUniformAttr &attr : graph.uniform_attrs.list) {
|
||||
ss << "vec4 attr" << attr.id << ";\n";
|
||||
}
|
||||
ss << "};\n\n";
|
||||
|
||||
/* TODO(fclem): Use the macro for length. Currently not working for EEVEE. */
|
||||
/* DRW_RESOURCE_CHUNK_LEN = 512 */
|
||||
info.uniform_buf(2, "UniformAttrs", GPU_ATTRIBUTE_UBO_BLOCK_NAME "[512]", Frequency::BATCH);
|
||||
}
|
||||
|
||||
if (!graph.layer_attrs.is_empty()) {
|
||||
info.additional_info("draw_layer_attributes");
|
||||
}
|
||||
|
||||
info.typedef_source_generated = ss.str();
|
||||
}
|
||||
|
||||
void GPUCodegen::node_serialize(Set<StringRefNull> &used_libraries,
|
||||
std::stringstream &eval_ss,
|
||||
const GPUNode *node)
|
||||
{
|
||||
gpu_material_library_use_function(used_libraries, node->name);
|
||||
|
||||
auto source_reference = [&](GPUInput *input) {
|
||||
BLI_assert(ELEM(input->source, GPU_SOURCE_OUTPUT, GPU_SOURCE_ATTR));
|
||||
/* These inputs can have non matching types. Do conversion. */
|
||||
GPUType to = input->type;
|
||||
GPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype :
|
||||
input->link->output->type;
|
||||
if (from != to) {
|
||||
/* Use defines declared inside codegen_lib (e.g. vec4_from_float). */
|
||||
eval_ss << to << "_from_" << from << "(";
|
||||
}
|
||||
|
||||
if (input->source == GPU_SOURCE_ATTR) {
|
||||
eval_ss << input;
|
||||
}
|
||||
else {
|
||||
eval_ss << input->link->output;
|
||||
}
|
||||
|
||||
if (from != to) {
|
||||
/* Special case that needs luminance coefficients as argument. */
|
||||
if (from == GPU_VEC4 && to == GPU_FLOAT) {
|
||||
float coefficients[3];
|
||||
IMB_colormanagement_get_luminance_coefficients(coefficients);
|
||||
eval_ss << ", " << Span<float>(coefficients, 3);
|
||||
}
|
||||
eval_ss << ")";
|
||||
}
|
||||
};
|
||||
|
||||
/* Declare constants. */
|
||||
for (GPUInput &input : node->inputs) {
|
||||
auto type = [&]() {
|
||||
/* Don't declare zone io variables twice. */
|
||||
std::stringstream ss;
|
||||
if (!input.is_duplicate) {
|
||||
ss << input.type;
|
||||
}
|
||||
return ss.str();
|
||||
};
|
||||
switch (input.source) {
|
||||
case GPU_SOURCE_FUNCTION_CALL:
|
||||
eval_ss << type() << " " << &input << "; " << input.function_call << &input << ");\n";
|
||||
break;
|
||||
case GPU_SOURCE_STRUCT:
|
||||
eval_ss << input.type << " " << &input << " = CLOSURE_DEFAULT;\n";
|
||||
break;
|
||||
case GPU_SOURCE_CONSTANT:
|
||||
if (!input.is_duplicate) {
|
||||
eval_ss << type() << " " << &input << " = " << static_cast<GPUConstant *>(&input)
|
||||
<< ";\n";
|
||||
}
|
||||
break;
|
||||
case GPU_SOURCE_OUTPUT:
|
||||
case GPU_SOURCE_ATTR:
|
||||
if (input.is_zone_io) {
|
||||
eval_ss << type() << " " << &input << " = ";
|
||||
source_reference(&input);
|
||||
eval_ss << ";\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (input.is_zone_io && (!input.is_duplicate || !input.link)) {
|
||||
eval_ss << type() << " zone" << input.id << " = " << &input << ";\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Declare temporary variables for node output storage. */
|
||||
for (GPUOutput &output : node->outputs) {
|
||||
if (output.is_zone_io) {
|
||||
break;
|
||||
}
|
||||
eval_ss << output.type << " " << &output << ";\n";
|
||||
}
|
||||
|
||||
/* Function call. */
|
||||
eval_ss << node->name << "(";
|
||||
/* Input arguments. */
|
||||
for (GPUInput &input : node->inputs) {
|
||||
if (input.is_zone_io) {
|
||||
break;
|
||||
}
|
||||
switch (input.source) {
|
||||
case GPU_SOURCE_OUTPUT:
|
||||
case GPU_SOURCE_ATTR: {
|
||||
source_reference(&input);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
eval_ss << &input;
|
||||
break;
|
||||
}
|
||||
GPUOutput *output = static_cast<GPUOutput *>(node->outputs.first);
|
||||
if ((input.next && !input.next->is_zone_io) || (output && !output->is_zone_io)) {
|
||||
eval_ss << ", ";
|
||||
}
|
||||
}
|
||||
/* Output arguments. */
|
||||
for (GPUOutput &output : node->outputs) {
|
||||
if (output.is_zone_io) {
|
||||
break;
|
||||
}
|
||||
eval_ss << &output;
|
||||
if (output.next && !output.next->is_zone_io) {
|
||||
eval_ss << ", ";
|
||||
}
|
||||
}
|
||||
eval_ss << ");\n\n";
|
||||
|
||||
/* Increment heuristic. */
|
||||
nodes_total_++;
|
||||
}
|
||||
|
||||
static Vector<StringRefNull> set_to_vector_stable(Set<StringRefNull> &set)
|
||||
{
|
||||
Vector<StringRefNull> source_files;
|
||||
for (const StringRefNull &str : set) {
|
||||
source_files.append(str);
|
||||
}
|
||||
/* Sort dependencies to avoid random order causing shader caching to fail (see #108289). */
|
||||
std::ranges::sort(source_files);
|
||||
return source_files;
|
||||
}
|
||||
|
||||
GPUGraphOutput GPUCodegen::graph_serialize(GPUNodeTag tree_tag,
|
||||
GPUNodeLink *output_link,
|
||||
const char *output_default)
|
||||
{
|
||||
if (output_link == nullptr && output_default == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Set<StringRefNull> used_libraries;
|
||||
std::stringstream eval_ss;
|
||||
bool has_nodes = false;
|
||||
/* NOTE: The node order is already top to bottom (or left to right in node editor)
|
||||
* because of the evaluation order inside ntreeExecGPUNodes(). */
|
||||
for (GPUNode &node : graph.nodes) {
|
||||
if ((node.tag & tree_tag) == 0) {
|
||||
continue;
|
||||
}
|
||||
node_serialize(used_libraries, eval_ss, &node);
|
||||
has_nodes = true;
|
||||
}
|
||||
|
||||
if (!has_nodes) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (output_link) {
|
||||
eval_ss << "return " << output_link->output << ";\n";
|
||||
}
|
||||
else {
|
||||
/* Default output in case there are only AOVs. */
|
||||
eval_ss << "return " << output_default << ";\n";
|
||||
}
|
||||
|
||||
std::string str = eval_ss.str();
|
||||
BLI_hash_mm2a_add(&hm2a_, reinterpret_cast<const uchar *>(str.c_str()), str.size());
|
||||
return {str, set_to_vector_stable(used_libraries)};
|
||||
}
|
||||
|
||||
GPUGraphOutput GPUCodegen::graph_serialize(GPUNodeTag tree_tag)
|
||||
{
|
||||
std::stringstream eval_ss;
|
||||
Set<StringRefNull> used_libraries;
|
||||
for (GPUNode &node : graph.nodes) {
|
||||
if (node.tag & tree_tag) {
|
||||
node_serialize(used_libraries, eval_ss, &node);
|
||||
}
|
||||
}
|
||||
std::string str = eval_ss.str();
|
||||
BLI_hash_mm2a_add(&hm2a_, reinterpret_cast<const uchar *>(str.c_str()), str.size());
|
||||
return {str, set_to_vector_stable(used_libraries)};
|
||||
}
|
||||
|
||||
void GPUCodegen::generate_cryptomatte()
|
||||
{
|
||||
cryptomatte_input_ = MEM_new_zeroed<GPUInput>(__func__);
|
||||
cryptomatte_input_->type = GPU_FLOAT;
|
||||
cryptomatte_input_->source = GPU_SOURCE_CRYPTOMATTE;
|
||||
|
||||
float material_hash = 0.0f;
|
||||
Material *material = GPU_material_get_material(&mat);
|
||||
if (material) {
|
||||
bke::cryptomatte::CryptomatteHash hash(material->id.name + 2,
|
||||
BLI_strnlen(material->id.name + 2, MAX_NAME - 2));
|
||||
material_hash = hash.float_encoded();
|
||||
}
|
||||
cryptomatte_input_->vec[0] = material_hash;
|
||||
|
||||
BLI_addtail(&ubo_inputs_, BLI_genericNodeN(cryptomatte_input_));
|
||||
}
|
||||
|
||||
void GPUCodegen::generate_uniform_buffer()
|
||||
{
|
||||
/* Extract uniform inputs. */
|
||||
for (GPUNode &node : graph.nodes) {
|
||||
for (GPUInput &input : node.inputs) {
|
||||
if (input.source == GPU_SOURCE_UNIFORM && !input.link) {
|
||||
/* We handle the UBO uniforms separately. */
|
||||
BLI_addtail(&ubo_inputs_, BLI_genericNodeN(&input));
|
||||
uniforms_total_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ubo_inputs_.is_empty()) {
|
||||
/* This sorts the inputs based on size. */
|
||||
GPU_material_uniform_buffer_create(&mat, &ubo_inputs_);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets id for unique names for all inputs, resources and temp variables. */
|
||||
void GPUCodegen::set_unique_ids()
|
||||
{
|
||||
Map<int, GPUNode *> zone_starts;
|
||||
Map<int, GPUNode *> zone_ends;
|
||||
|
||||
int id = 1;
|
||||
for (GPUNode &node : graph.nodes) {
|
||||
for (GPUInput &input : node.inputs) {
|
||||
input.id = id++;
|
||||
}
|
||||
for (GPUOutput &output : node.outputs) {
|
||||
output.id = id++;
|
||||
}
|
||||
if (node.zone_index != -1) {
|
||||
auto &map = node.is_zone_end ? zone_ends : zone_starts;
|
||||
map.add(node.zone_index, &node);
|
||||
}
|
||||
}
|
||||
|
||||
auto find_zone_io = [](auto first) {
|
||||
while (first && !first->is_zone_io && first->next) {
|
||||
first = first->next;
|
||||
}
|
||||
return first;
|
||||
};
|
||||
|
||||
/* Assign the same id to inputs and outputs of start and end zones. */
|
||||
for (GPUNode *end : zone_ends.values()) {
|
||||
|
||||
GPUInput *end_input = find_zone_io(static_cast<GPUInput *>(end->inputs.first));
|
||||
GPUOutput *end_output = find_zone_io(static_cast<GPUOutput *>(end->outputs.first));
|
||||
|
||||
GPUNode *start = zone_starts.lookup(end->zone_index);
|
||||
|
||||
GPUInput *start_input = find_zone_io(static_cast<GPUInput *>(start->inputs.first));
|
||||
GPUOutput *start_output = find_zone_io(static_cast<GPUOutput *>(start->outputs.first));
|
||||
|
||||
for (; start_input; start_input = start_input->next,
|
||||
start_output = start_output->next,
|
||||
end_input = end_input->next,
|
||||
end_output = end_output->next)
|
||||
{
|
||||
start_output->id = start_input->id;
|
||||
end_input->id = start_input->id;
|
||||
end_output->id = start_input->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPUCodegen::generate_graphs()
|
||||
{
|
||||
set_unique_ids();
|
||||
|
||||
output.surface = graph_serialize(
|
||||
GPU_NODE_TAG_SURFACE | GPU_NODE_TAG_AOV, graph.outlink_surface, "CLOSURE_DEFAULT");
|
||||
output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume, "CLOSURE_DEFAULT");
|
||||
output.displacement = graph_serialize(
|
||||
GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement, nullptr);
|
||||
output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness, nullptr);
|
||||
if (!graph.outlink_compositor.is_empty()) {
|
||||
output.composite = graph_serialize(GPU_NODE_TAG_COMPOSITOR);
|
||||
}
|
||||
|
||||
if (!graph.material_functions.is_empty()) {
|
||||
for (GPUNodeGraphFunctionLink &func_link : graph.material_functions) {
|
||||
std::stringstream eval_ss;
|
||||
/* Untag every node in the graph to avoid serializing nodes from other functions */
|
||||
for (GPUNode &node : graph.nodes) {
|
||||
node.tag &= ~GPU_NODE_TAG_FUNCTION;
|
||||
}
|
||||
/* Tag only the nodes needed for the current function */
|
||||
gpu_nodes_tag(&graph, func_link.outlink, GPU_NODE_TAG_FUNCTION);
|
||||
GPUGraphOutput graph = graph_serialize(GPU_NODE_TAG_FUNCTION, func_link.outlink);
|
||||
eval_ss << "float " << func_link.name << "() {\n" << graph.serialized << "}\n\n";
|
||||
output.material_functions.append({eval_ss.str(), graph.dependencies});
|
||||
}
|
||||
/* Leave the function tags as they were before serialization */
|
||||
for (GPUNodeGraphFunctionLink &funclink : graph.material_functions) {
|
||||
gpu_nodes_tag(&graph, funclink.outlink, GPU_NODE_TAG_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
for (GPUMaterialAttribute &attr : graph.attributes) {
|
||||
BLI_hash_mm2a_add(&hm2a_, reinterpret_cast<uchar *>(attr.name), strlen(attr.name));
|
||||
}
|
||||
|
||||
hash_ = BLI_hash_mm2a_end(&hm2a_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
103
blender-5.2.0/source/blender/gpu/intern/gpu_codegen.hh
Normal file
103
blender-5.2.0/source/blender/gpu/intern/gpu_codegen.hh
Normal file
@@ -0,0 +1,103 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Generate shader code from the intermediate node graph.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_hash_mm2a.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_vertex_format.hh"
|
||||
#include "gpu_node_graph.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace blender::gpu::shader {
|
||||
|
||||
struct GPUCodegenCreateInfo : ShaderCreateInfo {
|
||||
struct NameBuffer {
|
||||
using NameEntry = std::array<char, 32>;
|
||||
|
||||
/** Duplicate attribute names to avoid reference the GPUNodeGraph directly. */
|
||||
char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1];
|
||||
char var_names[16][8];
|
||||
Vector<std::unique_ptr<NameEntry>, 16> sampler_names;
|
||||
|
||||
/* Returns the appended name memory location */
|
||||
const char *append_sampler_name(const char name[32]);
|
||||
};
|
||||
|
||||
/** Optional generated interface. */
|
||||
StageInterfaceInfo *interface_generated = nullptr;
|
||||
/** Optional name buffer containing names referenced by StringRefNull. */
|
||||
NameBuffer name_buffer;
|
||||
|
||||
GPUCodegenCreateInfo(const char *name) : ShaderCreateInfo(name) {};
|
||||
|
||||
~GPUCodegenCreateInfo()
|
||||
{
|
||||
MEM_delete(interface_generated);
|
||||
}
|
||||
};
|
||||
|
||||
class GPUCodegen {
|
||||
public:
|
||||
GPUMaterial &mat;
|
||||
GPUNodeGraph &graph;
|
||||
GPUCodegenOutput output = {};
|
||||
GPUCodegenCreateInfo *create_info = nullptr;
|
||||
|
||||
private:
|
||||
uint32_t hash_ = 0;
|
||||
BLI_HashMurmur2A hm2a_;
|
||||
ListBaseT<LinkData> ubo_inputs_ = {nullptr, nullptr};
|
||||
GPUInput *cryptomatte_input_ = nullptr;
|
||||
|
||||
/** Cache parameters for complexity heuristic. */
|
||||
uint nodes_total_ = 0;
|
||||
uint textures_total_ = 0;
|
||||
uint uniforms_total_ = 0;
|
||||
|
||||
public:
|
||||
GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_, const char *debug_name);
|
||||
~GPUCodegen();
|
||||
|
||||
void generate_graphs();
|
||||
void generate_cryptomatte();
|
||||
void generate_uniform_buffer();
|
||||
void generate_attribs();
|
||||
void generate_resources();
|
||||
|
||||
uint32_t hash_get() const
|
||||
{
|
||||
return hash_;
|
||||
}
|
||||
|
||||
/* Heuristic determined during pass codegen for whether a
|
||||
* more optimal variant of this material should be compiled. */
|
||||
bool should_optimize_heuristic() const;
|
||||
|
||||
private:
|
||||
void set_unique_ids();
|
||||
|
||||
void node_serialize(Set<StringRefNull> &used_libraries,
|
||||
std::stringstream &eval_ss,
|
||||
const GPUNode *node);
|
||||
GPUGraphOutput graph_serialize(GPUNodeTag tree_tag,
|
||||
GPUNodeLink *output_link,
|
||||
const char *output_default = nullptr);
|
||||
GPUGraphOutput graph_serialize(GPUNodeTag tree_tag);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::shader
|
||||
44
blender-5.2.0/source/blender/gpu/intern/gpu_compute.cc
Normal file
44
blender-5.2.0/source/blender/gpu/intern/gpu_compute.cc
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "GPU_compute.hh"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_debug_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void GPU_compute_dispatch(gpu::Shader *shader,
|
||||
uint groups_x_len,
|
||||
uint groups_y_len,
|
||||
uint groups_z_len,
|
||||
const gpu::shader::SpecializationConstants *constants_state)
|
||||
{
|
||||
gpu::GPUBackend &gpu_backend = *gpu::GPUBackend::get();
|
||||
GPU_shader_bind(shader, constants_state);
|
||||
#ifndef NDEBUG
|
||||
gpu::debug_validate_binding_image_format();
|
||||
#endif
|
||||
gpu_backend.compute_dispatch(groups_x_len, groups_y_len, groups_z_len);
|
||||
}
|
||||
|
||||
void GPU_compute_dispatch_indirect(gpu::Shader *shader,
|
||||
gpu::StorageBuf *indirect_buf_,
|
||||
const gpu::shader::SpecializationConstants *constants_state)
|
||||
{
|
||||
gpu::GPUBackend &gpu_backend = *gpu::GPUBackend::get();
|
||||
gpu::StorageBuf *indirect_buf = reinterpret_cast<gpu::StorageBuf *>(indirect_buf_);
|
||||
|
||||
GPU_shader_bind(shader, constants_state);
|
||||
#ifndef NDEBUG
|
||||
gpu::debug_validate_binding_image_format();
|
||||
#endif
|
||||
gpu_backend.compute_dispatch_indirect(indirect_buf);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
779
blender-5.2.0/source/blender/gpu/intern/gpu_context.cc
Normal file
779
blender-5.2.0/source/blender/gpu/intern/gpu_context.cc
Normal file
@@ -0,0 +1,779 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Manage GL vertex array IDs in a thread-safe way
|
||||
* Use these instead of glGenBuffers & its friends
|
||||
* - alloc must be called from a thread that is bound
|
||||
* to the context that will be used for drawing with
|
||||
* this VAO.
|
||||
* - free can be called from any thread
|
||||
*/
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "GHOST_IContext.hh"
|
||||
#include "GHOST_ISystem.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_pass.hh"
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_matrix_private.hh"
|
||||
#include "gpu_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "vk_backend.hh"
|
||||
#endif
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
# include "gl_backend.hh"
|
||||
# include "gl_context.hh"
|
||||
#endif
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
# include "mtl_backend.hh"
|
||||
#endif
|
||||
#include "dummy_backend.hh"
|
||||
|
||||
#include "draw_debug.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
static thread_local Context *active_ctx = nullptr;
|
||||
|
||||
static Mutex backend_users_mutex;
|
||||
static int num_backend_users = 0;
|
||||
|
||||
static void gpu_backend_create();
|
||||
static void gpu_backend_discard();
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::Context methods
|
||||
* \{ */
|
||||
|
||||
namespace gpu {
|
||||
|
||||
Context::Context()
|
||||
{
|
||||
thread_ = pthread_self();
|
||||
is_active_ = false;
|
||||
matrix_state = GPU_matrix_state_create();
|
||||
texture_pool = GPUBackend::get()->texturepool_alloc();
|
||||
|
||||
context_id = Context::context_counter;
|
||||
Context::context_counter++;
|
||||
}
|
||||
|
||||
Context::~Context()
|
||||
{
|
||||
/* Derived class should have called free_resources already. */
|
||||
BLI_assert(front_left == nullptr);
|
||||
BLI_assert(back_left == nullptr);
|
||||
BLI_assert(front_right == nullptr);
|
||||
BLI_assert(back_right == nullptr);
|
||||
BLI_assert(texture_pool == nullptr);
|
||||
|
||||
/** IMPORTANT: Do not free resources (texture, batch, buffers) in this function. These objects
|
||||
* are likely to reference the GL/VK/MTLContext which is already destroyed at this point. */
|
||||
|
||||
GPU_matrix_state_discard(matrix_state);
|
||||
delete state_manager;
|
||||
delete imm;
|
||||
}
|
||||
|
||||
void Context::free_resources()
|
||||
{
|
||||
delete front_left;
|
||||
delete back_left;
|
||||
delete front_right;
|
||||
delete back_right;
|
||||
front_left = nullptr;
|
||||
back_left = nullptr;
|
||||
front_right = nullptr;
|
||||
back_right = nullptr;
|
||||
|
||||
GPU_BATCH_DISCARD_SAFE(procedural_points_batch);
|
||||
GPU_BATCH_DISCARD_SAFE(procedural_lines_batch);
|
||||
GPU_BATCH_DISCARD_SAFE(procedural_triangles_batch);
|
||||
GPU_BATCH_DISCARD_SAFE(procedural_triangle_strips_batch);
|
||||
GPU_VERTBUF_DISCARD_SAFE(dummy_vbo);
|
||||
|
||||
delete texture_pool;
|
||||
texture_pool = nullptr;
|
||||
}
|
||||
|
||||
bool Context::is_active_on_thread()
|
||||
{
|
||||
return (this == active_ctx) && pthread_equal(pthread_self(), thread_);
|
||||
}
|
||||
|
||||
Context *Context::get()
|
||||
{
|
||||
return active_ctx;
|
||||
}
|
||||
|
||||
VertBuf *Context::dummy_vbo_get()
|
||||
{
|
||||
if (this->dummy_vbo) {
|
||||
return this->dummy_vbo;
|
||||
}
|
||||
|
||||
/* TODO(fclem): get rid of this dummy VBO. */
|
||||
GPUVertFormat format = {0};
|
||||
GPU_vertformat_attr_add(&format, "dummy", gpu::VertAttrType::SFLOAT_32);
|
||||
this->dummy_vbo = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*this->dummy_vbo, 1);
|
||||
return this->dummy_vbo;
|
||||
}
|
||||
|
||||
Batch *Context::procedural_points_batch_get()
|
||||
{
|
||||
if (procedural_points_batch) {
|
||||
return procedural_points_batch;
|
||||
}
|
||||
|
||||
procedural_points_batch = GPU_batch_create(GPU_PRIM_POINTS, dummy_vbo_get(), nullptr);
|
||||
return procedural_points_batch;
|
||||
}
|
||||
|
||||
Batch *Context::procedural_lines_batch_get()
|
||||
{
|
||||
if (procedural_lines_batch) {
|
||||
return procedural_lines_batch;
|
||||
}
|
||||
|
||||
procedural_lines_batch = GPU_batch_create(GPU_PRIM_LINES, dummy_vbo_get(), nullptr);
|
||||
return procedural_lines_batch;
|
||||
}
|
||||
|
||||
Batch *Context::procedural_triangles_batch_get()
|
||||
{
|
||||
if (procedural_triangles_batch) {
|
||||
return procedural_triangles_batch;
|
||||
}
|
||||
|
||||
procedural_triangles_batch = GPU_batch_create(GPU_PRIM_TRIS, dummy_vbo_get(), nullptr);
|
||||
return procedural_triangles_batch;
|
||||
}
|
||||
|
||||
Batch *Context::procedural_triangle_strips_batch_get()
|
||||
{
|
||||
if (procedural_triangle_strips_batch) {
|
||||
return procedural_triangle_strips_batch;
|
||||
}
|
||||
|
||||
procedural_triangle_strips_batch = GPU_batch_create(
|
||||
GPU_PRIM_TRI_STRIP, dummy_vbo_get(), nullptr);
|
||||
return procedural_triangle_strips_batch;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
GPUContext *GPU_context_create(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context)
|
||||
{
|
||||
{
|
||||
std::scoped_lock lock(backend_users_mutex);
|
||||
if (num_backend_users == 0) {
|
||||
/* Automatically create backend when first context is created. */
|
||||
gpu_backend_create();
|
||||
}
|
||||
num_backend_users++;
|
||||
}
|
||||
|
||||
Context *ctx = GPUBackend::get()->context_alloc(ghost_window, ghost_context);
|
||||
|
||||
GPU_context_active_set(wrap(ctx));
|
||||
|
||||
draw::DebugDraw::get().acquire();
|
||||
|
||||
return wrap(ctx);
|
||||
}
|
||||
|
||||
void GPU_context_discard(GPUContext *ctx_)
|
||||
{
|
||||
Context *ctx = unwrap(ctx_);
|
||||
BLI_assert(active_ctx == ctx);
|
||||
|
||||
draw::DebugDraw::get().release();
|
||||
|
||||
GPUBackend *backend = GPUBackend::get();
|
||||
/* Flush any remaining printf while making sure we are inside render boundaries. */
|
||||
backend->render_begin();
|
||||
printf_end(ctx);
|
||||
backend->render_end();
|
||||
|
||||
delete ctx;
|
||||
active_ctx = nullptr;
|
||||
|
||||
{
|
||||
std::scoped_lock lock(backend_users_mutex);
|
||||
num_backend_users--;
|
||||
BLI_assert(num_backend_users >= 0);
|
||||
if (num_backend_users == 0) {
|
||||
/* Discard backend when last context is discarded. */
|
||||
gpu_backend_discard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_context_active_set(GPUContext *ctx_)
|
||||
{
|
||||
Context *ctx = unwrap(ctx_);
|
||||
|
||||
if (active_ctx) {
|
||||
GPU_shader_unbind();
|
||||
active_ctx->deactivate();
|
||||
}
|
||||
|
||||
active_ctx = ctx;
|
||||
|
||||
if (ctx) {
|
||||
ctx->activate();
|
||||
/* It can happen that the previous context drew with a different color-space.
|
||||
* In the case where the new context is drawing with the same shader that was previously bound
|
||||
* (shader binding optimization), the uniform would not be set again because the dirty flag
|
||||
* would not have been set (since the color space of this new context never changed). The
|
||||
* shader would reuse the same color-space as the previous context frame-buffer (see #137855).
|
||||
*/
|
||||
ctx->shader_builtin_srgb_is_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
GPUContext *GPU_context_active_get()
|
||||
{
|
||||
return wrap(Context::get());
|
||||
}
|
||||
|
||||
void GPU_context_begin_frame(GPUContext *ctx)
|
||||
{
|
||||
gpu::Context *_ctx = unwrap(ctx);
|
||||
if (_ctx) {
|
||||
_ctx->begin_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_context_end_frame(GPUContext *ctx)
|
||||
{
|
||||
gpu::Context *_ctx = unwrap(ctx);
|
||||
if (_ctx) {
|
||||
_ctx->end_frame();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_context_debug_pipeline_creation(GPUContext *ctx, bool enable)
|
||||
{
|
||||
gpu::Context *_ctx = unwrap(ctx);
|
||||
if (_ctx) {
|
||||
_ctx->debug_pipeline_creation = enable;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Main context global mutex
|
||||
*
|
||||
* Used to avoid crash on some old drivers.
|
||||
* \{ */
|
||||
|
||||
static Mutex main_context_mutex;
|
||||
|
||||
void GPU_context_main_lock()
|
||||
{
|
||||
main_context_mutex.lock();
|
||||
}
|
||||
|
||||
void GPU_context_main_unlock()
|
||||
{
|
||||
main_context_mutex.unlock();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Begin/end work blocks
|
||||
*
|
||||
* Used to explicitly define a per-frame block within which GPU work will happen.
|
||||
* Used for global autoreleasepool flushing in Metal
|
||||
* \{ */
|
||||
|
||||
void GPU_render_begin()
|
||||
{
|
||||
GPUBackend *backend = GPUBackend::get();
|
||||
BLI_assert(backend);
|
||||
/* WORKAROUND: Currently a band-aid for the heist production. Has no side effect for GL backend
|
||||
* but should be fixed for Metal. */
|
||||
if (backend) {
|
||||
backend->render_begin();
|
||||
printf_begin(active_ctx);
|
||||
}
|
||||
}
|
||||
void GPU_render_end()
|
||||
{
|
||||
GPUBackend *backend = GPUBackend::get();
|
||||
BLI_assert(backend);
|
||||
if (backend) {
|
||||
printf_end(active_ctx);
|
||||
backend->render_end();
|
||||
}
|
||||
}
|
||||
void GPU_render_step(bool force_resource_release)
|
||||
{
|
||||
GPUBackend *backend = GPUBackend::get();
|
||||
BLI_assert(backend);
|
||||
if (backend) {
|
||||
printf_end(active_ctx);
|
||||
backend->render_step(force_resource_release);
|
||||
printf_begin(active_ctx);
|
||||
}
|
||||
|
||||
GPU_pass_cache_update();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Backend selection
|
||||
* \{ */
|
||||
|
||||
static GPUBackendType g_backend_type = GPU_BACKEND_OPENGL;
|
||||
static std::optional<GPUBackendType> g_backend_type_override = std::nullopt;
|
||||
static std::optional<bool> g_backend_type_supported = std::nullopt;
|
||||
static std::optional<GHOST_GPUDevice> g_preferred_device_override = std::nullopt;
|
||||
static std::optional<int> g_vsync_override = std::nullopt;
|
||||
static GPUBackend *g_backend = nullptr;
|
||||
static GHOST_ISystem *g_ghost_system = nullptr;
|
||||
|
||||
void GPU_backend_ghost_system_set(GHOST_ISystem *ghost_system_handle)
|
||||
{
|
||||
g_ghost_system = ghost_system_handle;
|
||||
}
|
||||
|
||||
GHOST_ISystem *GPU_backend_ghost_system_get()
|
||||
{
|
||||
return g_ghost_system;
|
||||
}
|
||||
|
||||
void GPU_backend_type_selection_set(const GPUBackendType backend)
|
||||
{
|
||||
g_backend_type = backend;
|
||||
g_backend_type_supported = std::nullopt;
|
||||
}
|
||||
|
||||
int GPU_backend_vsync_get()
|
||||
{
|
||||
return g_vsync_override.value();
|
||||
}
|
||||
|
||||
void GPU_backend_vsync_set_override(const int vsync)
|
||||
{
|
||||
g_vsync_override = vsync;
|
||||
}
|
||||
|
||||
bool GPU_backend_vsync_is_overridden()
|
||||
{
|
||||
return g_vsync_override.has_value();
|
||||
}
|
||||
|
||||
GPUBackendType GPU_backend_type_selection_get()
|
||||
{
|
||||
return g_backend_type;
|
||||
}
|
||||
|
||||
void GPU_backend_type_selection_set_override(const GPUBackendType backend_type)
|
||||
{
|
||||
g_backend_type_override = backend_type;
|
||||
}
|
||||
|
||||
bool GPU_backend_type_selection_is_overridden()
|
||||
{
|
||||
return g_backend_type_override.has_value();
|
||||
}
|
||||
|
||||
void GPU_backend_preferred_device_set_override(const int index,
|
||||
const uint32_t vendor_id,
|
||||
const uint32_t device_id)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
|
||||
GHOST_GPUDevice device = {};
|
||||
device.is_override = true;
|
||||
device.fail_on_invalid_override = false;
|
||||
device.index = index;
|
||||
device.vendor_id = vendor_id;
|
||||
device.device_id = device_id;
|
||||
g_preferred_device_override = device;
|
||||
}
|
||||
|
||||
GHOST_GPUDevice GPU_backend_preferred_device_get()
|
||||
{
|
||||
if (g_preferred_device_override.has_value()) {
|
||||
GHOST_GPUDevice device = g_preferred_device_override.value();
|
||||
device.fail_on_invalid_override = (G.debug & G_DEBUG_GPU_DEVICE_NO_FALLBACK) != 0;
|
||||
device.fallback_index = U.gpu_preferred_index;
|
||||
device.fallback_vendor_id = U.gpu_preferred_vendor_id;
|
||||
device.fallback_device_id = U.gpu_preferred_device_id;
|
||||
return device;
|
||||
}
|
||||
|
||||
GHOST_GPUDevice device = {};
|
||||
device.is_override = false;
|
||||
device.fail_on_invalid_override = false;
|
||||
device.index = U.gpu_preferred_index;
|
||||
device.vendor_id = U.gpu_preferred_vendor_id;
|
||||
device.device_id = U.gpu_preferred_device_id;
|
||||
device.fallback_index = device.index;
|
||||
device.fallback_vendor_id = device.vendor_id;
|
||||
device.fallback_device_id = device.device_id;
|
||||
return device;
|
||||
}
|
||||
|
||||
static const char *gpu_backend_type_name(const GPUBackendType backend_type)
|
||||
{
|
||||
switch (backend_type) {
|
||||
case GPU_BACKEND_OPENGL:
|
||||
return "OpenGL";
|
||||
case GPU_BACKEND_VULKAN:
|
||||
return "Vulkan";
|
||||
case GPU_BACKEND_METAL:
|
||||
return "Metal";
|
||||
case GPU_BACKEND_NONE:
|
||||
return "None";
|
||||
case GPU_BACKEND_ANY:
|
||||
break;
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
void GPU_vulkan_supported_devices_print(FILE *fp)
|
||||
{
|
||||
blender::gpu::VKBackend::supported_devices_print(fp);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GPU_backend_type_selection_detect()
|
||||
{
|
||||
VectorSet<GPUBackendType> backends_to_check;
|
||||
if (g_backend_type_override.has_value()) {
|
||||
backends_to_check.add(*g_backend_type_override);
|
||||
}
|
||||
#if defined(WITH_OPENGL_BACKEND)
|
||||
backends_to_check.add(GPU_BACKEND_OPENGL);
|
||||
#elif defined(WITH_METAL_BACKEND)
|
||||
backends_to_check.add(GPU_BACKEND_METAL);
|
||||
#endif
|
||||
|
||||
#if defined(WITH_VULKAN_BACKEND)
|
||||
backends_to_check.add(GPU_BACKEND_VULKAN);
|
||||
#endif
|
||||
|
||||
for (const GPUBackendType backend_type : backends_to_check) {
|
||||
GPU_backend_type_selection_set(backend_type);
|
||||
if (GPU_backend_supported()) {
|
||||
if (g_preferred_device_override.has_value() && backend_type != GPU_BACKEND_VULKAN) {
|
||||
fprintf(stderr,
|
||||
"Warning: '--gpu-device' is only supported for the Vulkan backend. "
|
||||
"Ignoring the device override for the selected %s backend and falling back to the "
|
||||
"stored GPU preference.\n",
|
||||
gpu_backend_type_name(backend_type));
|
||||
g_preferred_device_override.reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
G.f |= G_FLAG_GPU_BACKEND_FALLBACK;
|
||||
}
|
||||
|
||||
GPU_backend_type_selection_set(GPU_BACKEND_NONE);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool gpu_backend_supported()
|
||||
{
|
||||
switch (g_backend_type) {
|
||||
case GPU_BACKEND_OPENGL:
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case GPU_BACKEND_VULKAN:
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
return VKBackend::is_supported();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case GPU_BACKEND_METAL:
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
return MTLBackend::metal_is_supported();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case GPU_BACKEND_NONE:
|
||||
return true;
|
||||
default:
|
||||
BLI_assert(false && "No backend specified");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GPU_backend_supported()
|
||||
{
|
||||
if (!g_backend_type_supported.has_value()) {
|
||||
g_backend_type_supported = gpu_backend_supported();
|
||||
}
|
||||
return *g_backend_type_supported;
|
||||
}
|
||||
|
||||
static void gpu_backend_create()
|
||||
{
|
||||
BLI_assert(g_backend == nullptr);
|
||||
BLI_assert(GPU_backend_supported());
|
||||
|
||||
switch (g_backend_type) {
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GPU_BACKEND_OPENGL:
|
||||
g_backend = MEM_new<GLBackend>(__func__);
|
||||
break;
|
||||
#endif
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GPU_BACKEND_VULKAN:
|
||||
g_backend = MEM_new<VKBackend>(__func__);
|
||||
break;
|
||||
#endif
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
case GPU_BACKEND_METAL:
|
||||
g_backend = MEM_new<MTLBackend>(__func__);
|
||||
break;
|
||||
#endif
|
||||
case GPU_BACKEND_NONE:
|
||||
g_backend = MEM_new<DummyBackend>(__func__);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gpu_backend_init_resources()
|
||||
{
|
||||
BLI_assert(g_backend);
|
||||
g_backend->init_resources();
|
||||
}
|
||||
|
||||
void gpu_backend_delete_resources()
|
||||
{
|
||||
BLI_assert(g_backend);
|
||||
g_backend->delete_resources();
|
||||
}
|
||||
|
||||
void gpu_backend_discard()
|
||||
{
|
||||
/* TODO: assert no resource left. */
|
||||
MEM_delete(g_backend);
|
||||
g_backend = nullptr;
|
||||
}
|
||||
|
||||
GPUBackendType GPU_backend_get_type()
|
||||
{
|
||||
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
if (g_backend && dynamic_cast<GLBackend *>(g_backend) != nullptr) {
|
||||
return GPU_BACKEND_OPENGL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
if (g_backend && dynamic_cast<MTLBackend *>(g_backend) != nullptr) {
|
||||
return GPU_BACKEND_METAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
if (g_backend && dynamic_cast<VKBackend *>(g_backend) != nullptr) {
|
||||
return GPU_BACKEND_VULKAN;
|
||||
}
|
||||
#endif
|
||||
|
||||
return GPU_BACKEND_NONE;
|
||||
}
|
||||
|
||||
const char *GPU_backend_get_name()
|
||||
{
|
||||
return gpu_backend_type_name(GPU_backend_get_type());
|
||||
}
|
||||
|
||||
GPUBackend *GPUBackend::get()
|
||||
{
|
||||
return g_backend;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUSecondaryContext
|
||||
* \{ */
|
||||
|
||||
namespace gpu {
|
||||
|
||||
static GHOST_TDrawingContextType ghost_context_type()
|
||||
{
|
||||
switch (GPU_backend_type_selection_get()) {
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GPU_BACKEND_OPENGL:
|
||||
return GHOST_kDrawingContextTypeOpenGL;
|
||||
#endif
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GPU_BACKEND_VULKAN:
|
||||
return GHOST_kDrawingContextTypeVulkan;
|
||||
#endif
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
case GPU_BACKEND_METAL:
|
||||
return GHOST_kDrawingContextTypeMetal;
|
||||
#endif
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return GHOST_kDrawingContextTypeNone;
|
||||
}
|
||||
}
|
||||
|
||||
GPUSecondaryContextData GPU_create_secondary_context()
|
||||
{
|
||||
/* Contexts can only be created on the main thread. */
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
GHOST_IContext *main_thread_ghost_context = GHOST_IContext::getActiveDrawingContext();
|
||||
GPUContext *main_thread_gpu_context = GPU_context_active_get();
|
||||
|
||||
/* GPU settings for context creation. */
|
||||
GHOST_GPUSettings gpu_settings = {0};
|
||||
gpu_settings.context_type = ghost_context_type();
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
gpu_settings.flags |= GHOST_gpuDebugContext;
|
||||
}
|
||||
gpu_settings.preferred_device = GPU_backend_preferred_device_get();
|
||||
|
||||
/* Grab the system handle. */
|
||||
GHOST_ISystem *ghost_system = GPU_backend_ghost_system_get();
|
||||
BLI_assert(ghost_system);
|
||||
|
||||
/* Create a Ghost GPU Context using the system handle. */
|
||||
GHOST_IContext *ghost_context = ghost_system->createOffscreenContext(gpu_settings);
|
||||
BLI_assert(ghost_context);
|
||||
|
||||
/* Activate it so GPU_context_create has a valid device for info queries. */
|
||||
ghost_context->activateDrawingContext();
|
||||
|
||||
/* Create a GPU context for the secondary thread to use. */
|
||||
GPUContext *gpu_context = GPU_context_create(nullptr, ghost_context);
|
||||
BLI_assert(gpu_context);
|
||||
|
||||
/* Release the Ghost GPU Context from this thread. */
|
||||
const GHOST_TSuccess success = ghost_context->releaseDrawingContext();
|
||||
BLI_assert(success);
|
||||
UNUSED_VARS_NDEBUG(success);
|
||||
|
||||
/* Restore the main thread contexts.
|
||||
* (required as the above context creation also makes it active).
|
||||
* Contexts can be unset when called from the event system (#157456) */
|
||||
if (main_thread_ghost_context) {
|
||||
main_thread_ghost_context->activateDrawingContext();
|
||||
}
|
||||
if (main_thread_gpu_context) {
|
||||
GPU_context_active_set(main_thread_gpu_context);
|
||||
}
|
||||
|
||||
return GPUSecondaryContextData{.ghost_context = ghost_context, .gpu_context = gpu_context};
|
||||
}
|
||||
|
||||
void GPU_activate_secondary_context(const GPUSecondaryContextData &data)
|
||||
{
|
||||
data.ghost_context->activateDrawingContext();
|
||||
GPU_context_active_set(data.gpu_context);
|
||||
}
|
||||
|
||||
void GPU_deactivate_secondary_context(const GPUSecondaryContextData &data)
|
||||
{
|
||||
GPU_context_active_set(nullptr);
|
||||
data.ghost_context->releaseDrawingContext();
|
||||
}
|
||||
|
||||
void GPU_destroy_secondary_context(GPUSecondaryContextData &data)
|
||||
{
|
||||
GHOST_IContext *cur_ghost_context = GHOST_IContext::getActiveDrawingContext();
|
||||
GPUContext *cur_gpu_context = GPU_context_active_get();
|
||||
|
||||
GPU_activate_secondary_context(data);
|
||||
|
||||
GPU_context_discard(data.gpu_context);
|
||||
|
||||
data.ghost_context->releaseDrawingContext();
|
||||
|
||||
GHOST_ISystem *ghost_system = GPU_backend_ghost_system_get();
|
||||
ghost_system->disposeContext(data.ghost_context);
|
||||
|
||||
/* Restore previous GHOST/GPU contexts, unless they are the same
|
||||
* as what is being destroyed. */
|
||||
if (cur_ghost_context && cur_ghost_context != data.ghost_context) {
|
||||
cur_ghost_context->activateDrawingContext();
|
||||
}
|
||||
if (cur_gpu_context != data.gpu_context) {
|
||||
GPU_context_active_set(cur_gpu_context);
|
||||
}
|
||||
|
||||
data.ghost_context = nullptr;
|
||||
data.gpu_context = nullptr;
|
||||
}
|
||||
|
||||
GPUSecondaryContext::GPUSecondaryContext()
|
||||
{
|
||||
data_ = GPU_create_secondary_context();
|
||||
}
|
||||
|
||||
GPUSecondaryContext::~GPUSecondaryContext()
|
||||
{
|
||||
/* Contexts should be destructed on the thread they were activated. */
|
||||
BLI_assert(!BLI_thread_is_main());
|
||||
|
||||
GPU_context_discard(data_.gpu_context);
|
||||
|
||||
data_.ghost_context->releaseDrawingContext();
|
||||
|
||||
GHOST_ISystem *ghost_system = GPU_backend_ghost_system_get();
|
||||
BLI_assert(ghost_system);
|
||||
ghost_system->disposeContext(data_.ghost_context);
|
||||
}
|
||||
|
||||
void GPUSecondaryContext::activate()
|
||||
{
|
||||
/* Contexts need to be activated in the thread they're going to be used. */
|
||||
BLI_assert(!BLI_thread_is_main());
|
||||
|
||||
data_.ghost_context->activateDrawingContext();
|
||||
GPU_context_active_set(data_.gpu_context);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
181
blender-5.2.0/source/blender/gpu/intern/gpu_context_private.hh
Normal file
181
blender-5.2.0/source/blender/gpu/intern/gpu_context_private.hh
Normal file
@@ -0,0 +1,181 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* This interface allow GPU to manage GL objects for multiple context and threads.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_texture_pool.hh"
|
||||
|
||||
#include "gpu_debug_private.hh"
|
||||
#include "gpu_framebuffer_private.hh"
|
||||
#include "gpu_immediate_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
#include "gpu_state_private.hh"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
class GHOST_IWindow;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUMatrixState;
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class Context {
|
||||
public:
|
||||
/** State management */
|
||||
Shader *shader = nullptr;
|
||||
FrameBuffer *active_fb = nullptr;
|
||||
GPUMatrixState *matrix_state = nullptr;
|
||||
StateManager *state_manager = nullptr;
|
||||
Immediate *imm = nullptr;
|
||||
|
||||
/**
|
||||
* All 4 window frame-buffers.
|
||||
* None of them are valid in an off-screen context.
|
||||
* Right frame-buffers are only available if using stereo rendering.
|
||||
* Front frame-buffers contains (in principle, but not always) the last frame color.
|
||||
* Default frame-buffer is back_left.
|
||||
*/
|
||||
FrameBuffer *back_left = nullptr;
|
||||
FrameBuffer *front_left = nullptr;
|
||||
FrameBuffer *back_right = nullptr;
|
||||
FrameBuffer *front_right = nullptr;
|
||||
|
||||
DebugStack debug_stack;
|
||||
bool debug_is_capturing = false;
|
||||
bool debug_pipeline_creation = false;
|
||||
|
||||
/* GPUContext counter used to assign a unique ID to each GPUContext.
|
||||
* NOTE(Metal): This is required by the Metal Backend, as a bug exists in the global OS shader
|
||||
* cache wherein compilation of identical source from two distinct threads can result in an
|
||||
* invalid cache collision, result in a broken shader object. Appending the unique context ID
|
||||
* onto compiled sources ensures the source hashes are different. */
|
||||
static inline std::atomic<int> context_counter = 0;
|
||||
int context_id = 0;
|
||||
|
||||
/* Used as a stack. Each render_begin/end pair will push pop from the stack. */
|
||||
Vector<StorageBuf *> printf_buf;
|
||||
|
||||
/** Dummy VBO to feed the procedural batches. */
|
||||
VertBuf *dummy_vbo = nullptr;
|
||||
/** Dummy batches for procedural geometry rendering. */
|
||||
Batch *procedural_points_batch = nullptr;
|
||||
Batch *procedural_lines_batch = nullptr;
|
||||
Batch *procedural_triangles_batch = nullptr;
|
||||
Batch *procedural_triangle_strips_batch = nullptr;
|
||||
|
||||
/** Texture pool used to recycle temporary texture (or render target) memory. */
|
||||
TexturePool *texture_pool = nullptr;
|
||||
|
||||
/** Global state to avoid setting the srgb builtin uniform for every shader bind. */
|
||||
int shader_builtin_srgb_transform = 0;
|
||||
bool shader_builtin_srgb_is_dirty = false;
|
||||
|
||||
protected:
|
||||
/** Thread on which this context is active. */
|
||||
pthread_t thread_;
|
||||
bool is_active_;
|
||||
/** Can be nullptr for off-screen contexts. */
|
||||
GHOST_IWindow *ghost_window_;
|
||||
|
||||
public:
|
||||
Context();
|
||||
virtual ~Context();
|
||||
|
||||
static Context *get();
|
||||
|
||||
virtual void activate() = 0;
|
||||
virtual void deactivate() = 0;
|
||||
virtual void begin_frame() = 0;
|
||||
virtual void end_frame() = 0;
|
||||
|
||||
/* Will push all pending commands to the GPU. */
|
||||
virtual void flush() = 0;
|
||||
/* Will wait until the GPU has finished executing all command. */
|
||||
virtual void finish() = 0;
|
||||
|
||||
virtual void memory_statistics_get(int *r_total_mem, int *r_free_mem) = 0;
|
||||
|
||||
virtual void debug_group_begin(const char * /*name*/, int /*index*/) {};
|
||||
virtual void debug_group_end() {};
|
||||
|
||||
/* Returns true if capture successfully started. */
|
||||
virtual bool debug_capture_begin(const char *title) = 0;
|
||||
virtual void debug_capture_end() = 0;
|
||||
virtual void *debug_capture_scope_create(const char *name) = 0;
|
||||
virtual bool debug_capture_scope_begin(void *scope) = 0;
|
||||
virtual void debug_capture_scope_end(void *scope) = 0;
|
||||
|
||||
/* Consider all buffers slot empty after these call for error checking.
|
||||
* But doesn't really free them. */
|
||||
virtual void debug_unbind_all_ubo() = 0;
|
||||
virtual void debug_unbind_all_ssbo() = 0;
|
||||
|
||||
bool is_active_on_thread();
|
||||
|
||||
VertBuf *dummy_vbo_get();
|
||||
Batch *procedural_points_batch_get();
|
||||
Batch *procedural_lines_batch_get();
|
||||
Batch *procedural_triangles_batch_get();
|
||||
Batch *procedural_triangle_strips_batch_get();
|
||||
|
||||
/* When using `--debug-gpu`, assert that the shader fragments write to all the writable
|
||||
* attachments of the bound frame-buffer. */
|
||||
void assert_framebuffer_shader_compatibility(Shader *sh)
|
||||
{
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(state_manager->state.write_mask & GPUWriteMask::GPU_WRITE_COLOR)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t fragment_output_bits = sh->fragment_output_bits;
|
||||
uint16_t fb_attachments_bits = active_fb->get_color_attachments_bitset();
|
||||
|
||||
if ((fb_attachments_bits & ~fragment_output_bits) != 0) {
|
||||
std::string msg;
|
||||
msg = msg + "Shader (" + sh->name_get() + ") does not write to all frame-buffer (" +
|
||||
active_fb->name_get() + ") color attachments";
|
||||
BLI_assert_msg(false, msg.c_str());
|
||||
std::cerr << msg << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Derived classes should call this from the destructor,
|
||||
* as freeing textures and frame-buffers may need the derived context to be valid.
|
||||
*/
|
||||
void free_resources();
|
||||
};
|
||||
|
||||
/* Syntactic sugar. */
|
||||
static inline GPUContext *wrap(Context *ctx)
|
||||
{
|
||||
return reinterpret_cast<GPUContext *>(ctx);
|
||||
}
|
||||
static inline Context *unwrap(GPUContext *ctx)
|
||||
{
|
||||
return reinterpret_cast<Context *>(ctx);
|
||||
}
|
||||
static inline const Context *unwrap(const GPUContext *ctx)
|
||||
{
|
||||
return reinterpret_cast<const Context *>(ctx);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
226
blender-5.2.0/source/blender/gpu/intern/gpu_debug.cc
Normal file
226
blender-5.2.0/source/blender/gpu/intern/gpu_debug.cc
Normal file
@@ -0,0 +1,226 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Debug features of OpenGL.
|
||||
*/
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
void GPU_debug_group_begin(const char *name)
|
||||
{
|
||||
if (!(G.debug & G_DEBUG_GPU) && !G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
Context *ctx = Context::get();
|
||||
DebugStack &stack = ctx->debug_stack;
|
||||
stack.append(StringRef(name));
|
||||
ctx->debug_group_begin(name, stack.size());
|
||||
}
|
||||
|
||||
void GPU_debug_group_end()
|
||||
{
|
||||
if (!(G.debug & G_DEBUG_GPU) && !G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
Context *ctx = Context::get();
|
||||
ctx->debug_stack.pop_last();
|
||||
ctx->debug_group_end();
|
||||
}
|
||||
|
||||
void GPU_debug_get_groups_names(int name_buf_len, char *r_name_buf)
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
if (ctx == nullptr) {
|
||||
return;
|
||||
}
|
||||
DebugStack &stack = ctx->debug_stack;
|
||||
if (stack.is_empty()) {
|
||||
r_name_buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
size_t len = 0;
|
||||
for (StringRef &name : stack) {
|
||||
len += BLI_snprintf_rlen(r_name_buf + len, name_buf_len - len, "%s > ", name.data());
|
||||
}
|
||||
r_name_buf[len - 3] = '\0';
|
||||
}
|
||||
|
||||
std::string GPU_debug_get_groups_names(IndexRange levels)
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
if (ctx == nullptr) {
|
||||
return "";
|
||||
}
|
||||
DebugStack &stack = ctx->debug_stack;
|
||||
if (stack.is_empty()) {
|
||||
return "";
|
||||
}
|
||||
std::string result;
|
||||
|
||||
int i = 0;
|
||||
for (StringRef &name : stack) {
|
||||
if (levels.contains(i++)) {
|
||||
result += name;
|
||||
result += " > ";
|
||||
}
|
||||
}
|
||||
return result.substr(0, result.size() - 3);
|
||||
}
|
||||
|
||||
bool GPU_debug_group_match(const char *ref)
|
||||
{
|
||||
/* Otherwise there will be no names. */
|
||||
BLI_assert(G.debug & G_DEBUG_GPU);
|
||||
Context *ctx = Context::get();
|
||||
if (ctx == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const DebugStack &stack = ctx->debug_stack;
|
||||
for (const StringRef &name : stack) {
|
||||
if (name == ref) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPU_debug_capture_begin(const char *title)
|
||||
{
|
||||
/* GPU Frame capture is only enabled when --debug-gpu is specified. */
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Context *ctx = Context::get();
|
||||
if (ctx && !ctx->debug_is_capturing) {
|
||||
ctx->debug_is_capturing = ctx->debug_capture_begin(title);
|
||||
/* Call GPU_finish to ensure all desired GPU commands occur within the capture boundary. */
|
||||
GPU_finish();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_debug_capture_end()
|
||||
{
|
||||
/* GPU Frame capture is only enabled when --debug-gpu is specified. */
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Context *ctx = Context::get();
|
||||
if (ctx && ctx->debug_is_capturing) {
|
||||
/* Call GPU_finish to ensure all desired GPU commands occur within the capture boundary. */
|
||||
GPU_finish();
|
||||
ctx->debug_capture_end();
|
||||
ctx->debug_is_capturing = false;
|
||||
}
|
||||
}
|
||||
|
||||
void *GPU_debug_capture_scope_create(const char *name)
|
||||
{
|
||||
/* GPU Frame capture is only enabled when --debug-gpu is specified. */
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Context *ctx = Context::get();
|
||||
if (!ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
return ctx->debug_capture_scope_create(name);
|
||||
}
|
||||
|
||||
bool GPU_debug_capture_scope_begin(void *scope)
|
||||
{
|
||||
/* Early exit if scope does not exist or not in debug mode. */
|
||||
if (!(G.debug & G_DEBUG_GPU) || !scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Context *ctx = Context::get();
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Declare beginning of capture scope region. */
|
||||
bool scope_capturing = ctx->debug_capture_scope_begin(scope);
|
||||
if (scope_capturing && !ctx->debug_is_capturing) {
|
||||
/* Call GPU_finish to ensure all desired GPU commands occur within the capture boundary. */
|
||||
GPU_finish();
|
||||
ctx->debug_is_capturing = true;
|
||||
}
|
||||
return ctx->debug_is_capturing;
|
||||
}
|
||||
|
||||
void GPU_debug_capture_scope_end(void *scope)
|
||||
{
|
||||
/* Early exit if scope does not exist or not in debug mode. */
|
||||
if (!(G.debug & G_DEBUG_GPU) || !scope) {
|
||||
return;
|
||||
}
|
||||
|
||||
Context *ctx = Context::get();
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If capturing, call GPU_finish to ensure all desired GPU commands occur within the capture
|
||||
* boundary. */
|
||||
if (ctx->debug_is_capturing) {
|
||||
GPU_finish();
|
||||
ctx->debug_is_capturing = false;
|
||||
}
|
||||
|
||||
/* Declare end of capture scope region. */
|
||||
ctx->debug_capture_scope_end(scope);
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
|
||||
void debug_validate_binding_image_format()
|
||||
{
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &texture_formats_state = Context::get()->state_manager->image_formats;
|
||||
const auto &texture_formats_shader = Context::get()->shader->interface->image_formats_;
|
||||
for (int image_unit = 0; image_unit < GPU_MAX_IMAGE; image_unit++) {
|
||||
TextureWriteFormat format_state = texture_formats_state[image_unit];
|
||||
TextureWriteFormat format_shader = texture_formats_shader[image_unit];
|
||||
if (format_state != TextureWriteFormat::Invalid &&
|
||||
format_shader == TextureWriteFormat::Invalid)
|
||||
{
|
||||
/* It is allowed for an image to be bound in the state manager but to be unused in the
|
||||
* shader. */
|
||||
continue;
|
||||
}
|
||||
if (UNLIKELY(texture_formats_shader[image_unit] != texture_formats_state[image_unit])) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error in GPU_debug_validate_binding_image_format: Image format mismatch detected for "
|
||||
"shader '%s' at binding %d (shader format '%s' vs. bound texture format '%s').\n",
|
||||
Context::get()->shader->name_get().c_str(),
|
||||
image_unit,
|
||||
GPU_texture_format_name(to_texture_format(texture_formats_shader[image_unit])),
|
||||
GPU_texture_format_name(to_texture_format(texture_formats_state[image_unit])));
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
22
blender-5.2.0/source/blender/gpu/intern/gpu_debug_private.hh
Normal file
22
blender-5.2.0/source/blender/gpu/intern/gpu_debug_private.hh
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* This interface allow GPU to manage GL objects for multiple context and threads.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
using DebugStack = Vector<StringRef>;
|
||||
|
||||
void debug_validate_binding_image_format();
|
||||
|
||||
} // namespace blender::gpu
|
||||
897
blender-5.2.0/source/blender/gpu/intern/gpu_framebuffer.cc
Normal file
897
blender-5.2.0/source/blender/gpu/intern/gpu_framebuffer.cc
Normal file
@@ -0,0 +1,897 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
#include "gpu_framebuffer_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.framebuffer"};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Constructor / Destructor
|
||||
* \{ */
|
||||
|
||||
FrameBuffer::FrameBuffer(const char *name)
|
||||
{
|
||||
if (name) {
|
||||
STRNCPY(name_, name);
|
||||
}
|
||||
else {
|
||||
name_[0] = '\0';
|
||||
}
|
||||
/* Force config on first use. */
|
||||
dirty_attachments_ = true;
|
||||
dirty_state_ = true;
|
||||
|
||||
for (GPUAttachment &attachment : attachments_) {
|
||||
attachment.tex = nullptr;
|
||||
attachment.mip = -1;
|
||||
attachment.layer = -1;
|
||||
}
|
||||
}
|
||||
|
||||
FrameBuffer::~FrameBuffer()
|
||||
{
|
||||
for (GPUAttachment &attachment : attachments_) {
|
||||
if (attachment.tex != nullptr) {
|
||||
reinterpret_cast<Texture *>(attachment.tex)->detach_from(this);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
if (this->py_ref) {
|
||||
*this->py_ref = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Attachments Management
|
||||
* \{ */
|
||||
|
||||
void FrameBuffer::attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment)
|
||||
{
|
||||
if (new_attachment.mip == -1) {
|
||||
return; /* GPU_ATTACHMENT_LEAVE */
|
||||
}
|
||||
|
||||
if (type >= GPU_FB_MAX_ATTACHMENT) {
|
||||
fprintf(stderr,
|
||||
"GPUFramebuffer: Error: Trying to attach texture to type %d but maximum slot is %d.\n",
|
||||
type - GPU_FB_COLOR_ATTACHMENT0,
|
||||
GPU_FB_MAX_COLOR_ATTACHMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_attachment.tex) {
|
||||
if (new_attachment.layer > 0) {
|
||||
BLI_assert(GPU_texture_is_cube(new_attachment.tex) ||
|
||||
GPU_texture_is_array(new_attachment.tex));
|
||||
}
|
||||
if (GPU_texture_has_stencil_format(new_attachment.tex)) {
|
||||
BLI_assert(ELEM(type, GPU_FB_DEPTH_STENCIL_ATTACHMENT));
|
||||
}
|
||||
else if (GPU_texture_has_depth_format(new_attachment.tex)) {
|
||||
BLI_assert(ELEM(type, GPU_FB_DEPTH_ATTACHMENT));
|
||||
}
|
||||
}
|
||||
|
||||
GPUAttachment &attachment = attachments_[type];
|
||||
|
||||
set_color_attachment_bit(type, new_attachment.tex != nullptr);
|
||||
|
||||
if (attachment.tex == new_attachment.tex && attachment.layer == new_attachment.layer &&
|
||||
attachment.mip == new_attachment.mip)
|
||||
{
|
||||
return; /* Exact same texture already bound here. */
|
||||
}
|
||||
/* Unbind previous and bind new. */
|
||||
/* TODO(fclem): cleanup the casts. */
|
||||
if (attachment.tex) {
|
||||
reinterpret_cast<Texture *>(attachment.tex)->detach_from(this);
|
||||
}
|
||||
|
||||
/* Might be null if this is for unbinding. */
|
||||
if (new_attachment.tex) {
|
||||
reinterpret_cast<Texture *>(new_attachment.tex)->attach_to(this, type);
|
||||
}
|
||||
else {
|
||||
/* GPU_ATTACHMENT_NONE */
|
||||
}
|
||||
|
||||
attachment = new_attachment;
|
||||
dirty_attachments_ = true;
|
||||
}
|
||||
|
||||
void FrameBuffer::attachment_remove(GPUAttachmentType type)
|
||||
{
|
||||
attachments_[type] = GPU_ATTACHMENT_NONE;
|
||||
dirty_attachments_ = true;
|
||||
set_color_attachment_bit(type, false);
|
||||
}
|
||||
|
||||
void FrameBuffer::subpass_transition(const GPUAttachmentState depth_attachment_state,
|
||||
Span<GPUAttachmentState> color_attachment_states)
|
||||
{
|
||||
/* NOTE: Depth is not supported as input attachment because the Metal API doesn't support it and
|
||||
* because depth is not compatible with the framebuffer fetch implementation. */
|
||||
BLI_assert(depth_attachment_state != GPU_ATTACHMENT_READ);
|
||||
|
||||
if (!attachments_[GPU_FB_DEPTH_ATTACHMENT].tex &&
|
||||
!attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex)
|
||||
{
|
||||
BLI_assert(depth_attachment_state == GPU_ATTACHMENT_IGNORE);
|
||||
}
|
||||
|
||||
BLI_assert(color_attachment_states.size() <= GPU_FB_MAX_COLOR_ATTACHMENT);
|
||||
for (int i : IndexRange(GPU_FB_MAX_COLOR_ATTACHMENT)) {
|
||||
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
|
||||
if (this->attachments_[type].tex) {
|
||||
BLI_assert(i < color_attachment_states.size());
|
||||
set_color_attachment_bit(type, color_attachment_states[i] == GPU_ATTACHMENT_WRITE);
|
||||
}
|
||||
else {
|
||||
set_color_attachment_bit(type, false);
|
||||
BLI_assert(i >= color_attachment_states.size() ||
|
||||
color_attachment_states[i] != GPU_ATTACHMENT_READ);
|
||||
}
|
||||
}
|
||||
|
||||
subpass_transition_impl(depth_attachment_state, color_attachment_states);
|
||||
}
|
||||
|
||||
void FrameBuffer::load_store_config_array(const GPULoadStore *load_store_actions, uint actions_len)
|
||||
{
|
||||
/* Follows attachment structure of GPU_framebuffer_config_array/GPU_framebuffer_ensure_config */
|
||||
const GPULoadStore &depth_action = load_store_actions[0];
|
||||
Span<GPULoadStore> color_attachment_actions(load_store_actions + 1, actions_len - 1);
|
||||
BLI_assert(color_attachment_actions.size() <= GPU_FB_MAX_COLOR_ATTACHMENT);
|
||||
|
||||
if (!attachments_[GPU_FB_DEPTH_ATTACHMENT].tex &&
|
||||
!attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex)
|
||||
{
|
||||
BLI_assert(depth_action.load_action == GPU_LOADACTION_DONT_CARE &&
|
||||
depth_action.store_action == GPU_STOREACTION_DONT_CARE);
|
||||
}
|
||||
|
||||
if (this->attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex) {
|
||||
this->attachment_set_loadstore_op(GPU_FB_DEPTH_STENCIL_ATTACHMENT, depth_action);
|
||||
}
|
||||
|
||||
if (this->attachments_[GPU_FB_DEPTH_ATTACHMENT].tex) {
|
||||
this->attachment_set_loadstore_op(GPU_FB_DEPTH_ATTACHMENT, depth_action);
|
||||
}
|
||||
|
||||
for (int i : IndexRange(GPU_FB_MAX_COLOR_ATTACHMENT)) {
|
||||
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
|
||||
if (this->attachments_[type].tex) {
|
||||
BLI_assert(i < color_attachment_actions.size());
|
||||
this->attachment_set_loadstore_op(type, color_attachment_actions[i]);
|
||||
}
|
||||
else {
|
||||
BLI_assert(i >= color_attachment_actions.size() ||
|
||||
(color_attachment_actions[i].load_action == GPU_LOADACTION_DONT_CARE &&
|
||||
color_attachment_actions[i].store_action == GPU_STOREACTION_DONT_CARE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint FrameBuffer::get_bits_per_pixel()
|
||||
{
|
||||
uint total_bits = 0;
|
||||
for (GPUAttachment &attachment : attachments_) {
|
||||
Texture *tex = reinterpret_cast<Texture *>(attachment.tex);
|
||||
if (tex != nullptr) {
|
||||
int bits = to_bytesize(tex->format_get()) * to_component_len(tex->format_get());
|
||||
total_bits += bits;
|
||||
}
|
||||
}
|
||||
return total_bits;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name C-API
|
||||
* \{ */
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
gpu::FrameBuffer *GPU_framebuffer_create(const char *name)
|
||||
{
|
||||
/* We generate the FB object later at first use in order to
|
||||
* create the frame-buffer in the right opengl context. */
|
||||
return GPUBackend::get()->framebuffer_alloc(name);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_free(gpu::FrameBuffer *fb)
|
||||
{
|
||||
delete fb;
|
||||
}
|
||||
|
||||
const char *GPU_framebuffer_get_name(gpu::FrameBuffer *fb)
|
||||
{
|
||||
return fb->name_get();
|
||||
}
|
||||
|
||||
/* ---------- Binding ----------- */
|
||||
|
||||
void GPU_framebuffer_bind(gpu::FrameBuffer *fb)
|
||||
{
|
||||
const bool enable_srgb = true;
|
||||
/* Disable custom loadstore and bind. */
|
||||
fb->set_use_explicit_loadstore(false);
|
||||
fb->bind(enable_srgb);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_bind_loadstore(gpu::FrameBuffer *fb,
|
||||
const GPULoadStore *load_store_actions,
|
||||
uint actions_len)
|
||||
{
|
||||
const bool enable_srgb = true;
|
||||
/* Bind with explicit loadstore state */
|
||||
fb->set_use_explicit_loadstore(true);
|
||||
fb->bind(enable_srgb);
|
||||
|
||||
/* Update load store */
|
||||
fb->load_store_config_array(load_store_actions, actions_len);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_subpass_transition_array(gpu::FrameBuffer *fb,
|
||||
const GPUAttachmentState *attachment_states,
|
||||
uint attachment_len)
|
||||
{
|
||||
fb->subpass_transition(attachment_states[0],
|
||||
Span<GPUAttachmentState>(attachment_states + 1, attachment_len - 1));
|
||||
}
|
||||
|
||||
void GPU_framebuffer_bind_no_srgb(gpu::FrameBuffer *fb)
|
||||
{
|
||||
const bool enable_srgb = false;
|
||||
fb->bind(enable_srgb);
|
||||
}
|
||||
|
||||
void GPU_backbuffer_bind(GPUBackBuffer back_buffer_type)
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
|
||||
if (back_buffer_type == GPU_BACKBUFFER_LEFT) {
|
||||
ctx->back_left->bind(false);
|
||||
}
|
||||
else {
|
||||
ctx->back_right->bind(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_framebuffer_restore()
|
||||
{
|
||||
Context::get()->back_left->bind(false);
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *GPU_framebuffer_active_get()
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
return ctx ? ctx->active_fb : nullptr;
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *GPU_framebuffer_back_get()
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
return ctx ? ctx->back_left : nullptr;
|
||||
}
|
||||
|
||||
bool GPU_framebuffer_bound(gpu::FrameBuffer *gpu_fb)
|
||||
{
|
||||
return (gpu_fb == GPU_framebuffer_active_get());
|
||||
}
|
||||
|
||||
/* ---------- Attachment Management ----------- */
|
||||
|
||||
bool GPU_framebuffer_check_valid(gpu::FrameBuffer *gpu_fb, char err_out[256])
|
||||
{
|
||||
return gpu_fb->check(err_out);
|
||||
}
|
||||
|
||||
static void gpu_framebuffer_texture_attach_ex(gpu::FrameBuffer *gpu_fb,
|
||||
GPUAttachment attachment,
|
||||
int slot)
|
||||
{
|
||||
Texture *tex = reinterpret_cast<Texture *>(attachment.tex);
|
||||
GPUAttachmentType type = tex->attachment_type(slot);
|
||||
gpu_fb->attachment_set(type, attachment);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_texture_attach(gpu::FrameBuffer *fb, gpu::Texture *tex, int slot, int mip)
|
||||
{
|
||||
GPUAttachment attachment = GPU_ATTACHMENT_TEXTURE_MIP(tex, mip);
|
||||
gpu_framebuffer_texture_attach_ex(fb, attachment, slot);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_texture_layer_attach(
|
||||
gpu::FrameBuffer *fb, gpu::Texture *tex, int slot, int layer, int mip)
|
||||
{
|
||||
GPUAttachment attachment = GPU_ATTACHMENT_TEXTURE_LAYER_MIP(tex, layer, mip);
|
||||
gpu_framebuffer_texture_attach_ex(fb, attachment, slot);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_texture_cubeface_attach(
|
||||
gpu::FrameBuffer *fb, gpu::Texture *tex, int slot, int face, int mip)
|
||||
{
|
||||
GPUAttachment attachment = GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(tex, face, mip);
|
||||
gpu_framebuffer_texture_attach_ex(fb, attachment, slot);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_texture_detach(gpu::FrameBuffer *fb, gpu::Texture *tex)
|
||||
{
|
||||
tex->detach_from(fb);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_config_array(gpu::FrameBuffer *fb,
|
||||
const GPUAttachment *config,
|
||||
int config_len)
|
||||
{
|
||||
const GPUAttachment &depth_attachment = config[0];
|
||||
Span<GPUAttachment> color_attachments(config + 1, config_len - 1);
|
||||
|
||||
if (depth_attachment.mip == -1) {
|
||||
/* GPU_ATTACHMENT_LEAVE */
|
||||
}
|
||||
else if (depth_attachment.tex == nullptr) {
|
||||
/* GPU_ATTACHMENT_NONE: Need to clear both targets. */
|
||||
fb->attachment_set(GPU_FB_DEPTH_STENCIL_ATTACHMENT, depth_attachment);
|
||||
fb->attachment_set(GPU_FB_DEPTH_ATTACHMENT, depth_attachment);
|
||||
}
|
||||
else {
|
||||
GPUAttachmentType type = GPU_texture_has_stencil_format(depth_attachment.tex) ?
|
||||
GPU_FB_DEPTH_STENCIL_ATTACHMENT :
|
||||
GPU_FB_DEPTH_ATTACHMENT;
|
||||
fb->attachment_set(type, depth_attachment);
|
||||
}
|
||||
|
||||
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0;
|
||||
for (const GPUAttachment &attachment : color_attachments) {
|
||||
fb->attachment_set(type, attachment);
|
||||
++type;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_framebuffer_default_size(gpu::FrameBuffer *gpu_fb, int width, int height)
|
||||
{
|
||||
gpu_fb->default_size_set(width, height);
|
||||
}
|
||||
|
||||
int2 GPU_framebuffer_extent_get(gpu::FrameBuffer *gpu_fb)
|
||||
{
|
||||
return gpu_fb->size_get();
|
||||
}
|
||||
|
||||
/* ---------- Viewport & Scissor Region ----------- */
|
||||
|
||||
void GPU_framebuffer_viewport_set(gpu::FrameBuffer *gpu_fb, int x, int y, int width, int height)
|
||||
{
|
||||
int viewport_rect[4] = {x, y, width, height};
|
||||
gpu_fb->viewport_set(viewport_rect);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_multi_viewports_set(gpu::FrameBuffer *gpu_fb,
|
||||
const int viewport_rects[GPU_MAX_VIEWPORTS][4])
|
||||
{
|
||||
gpu_fb->viewport_multi_set(viewport_rects);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_viewport_get(gpu::FrameBuffer *gpu_fb, int r_viewport[4])
|
||||
{
|
||||
gpu_fb->viewport_get(r_viewport);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_viewport_reset(gpu::FrameBuffer *gpu_fb)
|
||||
{
|
||||
gpu_fb->viewport_reset();
|
||||
}
|
||||
|
||||
/* ---------- Frame-buffer Operations ----------- */
|
||||
|
||||
/* Check for any loss of data when casting. */
|
||||
static void check_clear_color(const Texture *tex, double4 clear_value, int attachment)
|
||||
{
|
||||
GPUTextureFormatFlag flag = tex->format_flag_get();
|
||||
|
||||
if ((flag & GPU_FORMAT_INTEGER) && (flag & GPU_FORMAT_SIGNED)) {
|
||||
int4 data = int4(clear_value);
|
||||
if (double4(data) != clear_value) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Lossy conversion when clearing integer attachment %d: "
|
||||
"(%f, %f, %f, %f) > (%d, %d, %d, %d)",
|
||||
attachment,
|
||||
UNPACK4(clear_value),
|
||||
UNPACK4(data));
|
||||
}
|
||||
}
|
||||
else if ((flag & GPU_FORMAT_INTEGER) && !(flag & GPU_FORMAT_SIGNED)) {
|
||||
uint4 data = uint4(clear_value);
|
||||
if (double4(data) != clear_value) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Lossy conversion when clearing unsigned integer attachment %d: "
|
||||
"(%f, %f, %f, %f) > (%u, %u, %u, %u)",
|
||||
attachment,
|
||||
UNPACK4(clear_value),
|
||||
UNPACK4(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_framebuffer_clear(gpu::FrameBuffer *gpu_fb,
|
||||
GPUFrameBufferBits buffers,
|
||||
const double4 clear_col,
|
||||
float clear_depth,
|
||||
uint clear_stencil)
|
||||
{
|
||||
BLI_assert_msg(gpu_fb->get_use_explicit_loadstore() == false,
|
||||
"Using GPU_framebuffer_clear_* functions in conjunction with custom load-store "
|
||||
"state via GPU_framebuffer_bind_ex is invalid.");
|
||||
|
||||
if ((G.debug & G_DEBUG_GPU) && (buffers & GPU_COLOR_BIT)) {
|
||||
for (int i = 0; i < GPU_FB_MAX_COLOR_ATTACHMENT; i++) {
|
||||
Texture *tex = gpu_fb->color_tex(i);
|
||||
if (tex != nullptr) {
|
||||
check_clear_color(tex, clear_col, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpu_fb->clear(buffers, clear_col, clear_depth, clear_stencil);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_clear_color(gpu::FrameBuffer *fb, const double4 clear_col)
|
||||
{
|
||||
GPU_framebuffer_clear(fb, GPU_COLOR_BIT, clear_col, 0.0f, 0x00);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_clear_depth(gpu::FrameBuffer *fb, float clear_depth)
|
||||
{
|
||||
GPU_framebuffer_clear(fb, GPU_DEPTH_BIT, double4{}, clear_depth, 0x00);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_clear_color_depth(gpu::FrameBuffer *fb,
|
||||
const double4 clear_col,
|
||||
float clear_depth)
|
||||
{
|
||||
GPU_framebuffer_clear(fb, GPU_COLOR_BIT | GPU_DEPTH_BIT, clear_col, clear_depth, 0x00);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_clear_stencil(gpu::FrameBuffer *fb, uint clear_stencil)
|
||||
{
|
||||
GPU_framebuffer_clear(fb, GPU_STENCIL_BIT, double4{}, 0.0f, clear_stencil);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_clear_depth_stencil(gpu::FrameBuffer *fb,
|
||||
float clear_depth,
|
||||
uint clear_stencil)
|
||||
{
|
||||
GPU_framebuffer_clear(
|
||||
fb, GPU_DEPTH_BIT | GPU_STENCIL_BIT, double4{}, clear_depth, clear_stencil);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_clear_color_depth_stencil(gpu::FrameBuffer *fb,
|
||||
const double4 clear_col,
|
||||
float clear_depth,
|
||||
uint clear_stencil)
|
||||
{
|
||||
GPU_framebuffer_clear(
|
||||
fb, GPU_COLOR_BIT | GPU_DEPTH_BIT | GPU_STENCIL_BIT, clear_col, clear_depth, clear_stencil);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_multi_clear(gpu::FrameBuffer *fb, Span<double4> clear_colors)
|
||||
{
|
||||
BLI_assert_msg(fb->get_use_explicit_loadstore() == false,
|
||||
"Using GPU_framebuffer_clear_* functions in conjunction with custom load-store "
|
||||
"state via GPU_framebuffer_bind_ex is invalid.");
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
for (int i = 0; i < GPU_FB_MAX_COLOR_ATTACHMENT; i++) {
|
||||
Texture *tex = fb->color_tex(i);
|
||||
if (tex != nullptr) {
|
||||
check_clear_color(tex, clear_colors[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fb->clear_multi(clear_colors);
|
||||
}
|
||||
|
||||
void GPU_clear_color(float red, float green, float blue, float alpha)
|
||||
{
|
||||
BLI_assert_msg(Context::get()->active_fb->get_use_explicit_loadstore() == false,
|
||||
"Using GPU_framebuffer_clear_* functions in conjunction with custom load-store "
|
||||
"state via GPU_framebuffer_bind_ex is invalid.");
|
||||
double4 clear_col = {red, green, blue, alpha};
|
||||
Context::get()->active_fb->clear(GPU_COLOR_BIT, clear_col, 0.0f, 0x0);
|
||||
}
|
||||
|
||||
void GPU_clear_depth(float depth)
|
||||
{
|
||||
BLI_assert_msg(Context::get()->active_fb->get_use_explicit_loadstore() == false,
|
||||
"Using GPU_framebuffer_clear_* functions in conjunction with custom load-store "
|
||||
"state via GPU_framebuffer_bind_ex is invalid.");
|
||||
Context::get()->active_fb->clear(GPU_DEPTH_BIT, double4{}, depth, 0x0);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_read_depth(
|
||||
gpu::FrameBuffer *fb, int x, int y, int w, int h, eGPUDataFormat format, void *data)
|
||||
{
|
||||
int rect[4] = {x, y, w, h};
|
||||
fb->read(GPU_DEPTH_BIT, format, rect, 1, 1, data);
|
||||
}
|
||||
|
||||
void GPU_framebuffer_read_color(gpu::FrameBuffer *fb,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
int h,
|
||||
int channels,
|
||||
int slot,
|
||||
eGPUDataFormat format,
|
||||
void *data)
|
||||
{
|
||||
int rect[4] = {x, y, w, h};
|
||||
fb->read(GPU_COLOR_BIT, format, rect, channels, slot, data);
|
||||
}
|
||||
|
||||
void GPU_frontbuffer_read_color(
|
||||
int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data)
|
||||
{
|
||||
int rect[4] = {x, y, w, h};
|
||||
Context::get()->front_left->read(GPU_COLOR_BIT, format, rect, channels, 0, data);
|
||||
}
|
||||
|
||||
/* TODO(fclem): port as texture operation. */
|
||||
void GPU_framebuffer_blit(gpu::FrameBuffer *fb_read,
|
||||
int read_slot,
|
||||
gpu::FrameBuffer *fb_write,
|
||||
int write_slot,
|
||||
GPUFrameBufferBits blit_buffers)
|
||||
{
|
||||
BLI_assert(blit_buffers != 0);
|
||||
|
||||
FrameBuffer *prev_fb = Context::get()->active_fb;
|
||||
|
||||
#ifndef NDEBUG
|
||||
gpu::Texture *read_tex, *write_tex;
|
||||
if (blit_buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) {
|
||||
read_tex = fb_read->depth_tex();
|
||||
write_tex = fb_write->depth_tex();
|
||||
}
|
||||
else {
|
||||
read_tex = fb_read->color_tex(read_slot);
|
||||
write_tex = fb_write->color_tex(write_slot);
|
||||
}
|
||||
|
||||
if (blit_buffers & GPU_DEPTH_BIT) {
|
||||
BLI_assert(GPU_texture_has_depth_format(read_tex) && GPU_texture_has_depth_format(write_tex));
|
||||
BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex) ||
|
||||
(ELEM(GPU_texture_format(read_tex),
|
||||
TextureFormat::SFLOAT_32_DEPTH,
|
||||
TextureFormat::SFLOAT_32_DEPTH_UINT_8) &&
|
||||
ELEM(GPU_texture_format(write_tex),
|
||||
TextureFormat::SFLOAT_32_DEPTH,
|
||||
TextureFormat::SFLOAT_32_DEPTH_UINT_8)));
|
||||
}
|
||||
if (blit_buffers & GPU_STENCIL_BIT) {
|
||||
BLI_assert(GPU_texture_has_stencil_format(read_tex) &&
|
||||
GPU_texture_has_stencil_format(write_tex));
|
||||
BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0);
|
||||
|
||||
/* FIXME(@fclem): sRGB is not saved. */
|
||||
prev_fb->bind(true);
|
||||
}
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
void **GPU_framebuffer_py_reference_get(gpu::FrameBuffer *fb)
|
||||
{
|
||||
return fb->py_ref;
|
||||
}
|
||||
|
||||
void GPU_framebuffer_py_reference_set(gpu::FrameBuffer *fb, void **py_ref)
|
||||
{
|
||||
BLI_assert(py_ref == nullptr || fb->py_ref == nullptr);
|
||||
fb->py_ref = py_ref;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Frame-Buffer Stack
|
||||
*
|
||||
* Keeps track of frame-buffer binding operation to restore previously bound frame-buffers.
|
||||
* \{ */
|
||||
|
||||
#define FRAMEBUFFER_STACK_DEPTH 16
|
||||
|
||||
static struct {
|
||||
gpu::FrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH];
|
||||
uint top;
|
||||
} FrameBufferStack = {{nullptr}};
|
||||
|
||||
void GPU_framebuffer_push(gpu::FrameBuffer *fb)
|
||||
{
|
||||
BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH);
|
||||
FrameBufferStack.framebuffers[FrameBufferStack.top] = fb;
|
||||
FrameBufferStack.top++;
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *GPU_framebuffer_pop()
|
||||
{
|
||||
BLI_assert(FrameBufferStack.top > 0);
|
||||
FrameBufferStack.top--;
|
||||
return FrameBufferStack.framebuffers[FrameBufferStack.top];
|
||||
}
|
||||
|
||||
uint GPU_framebuffer_stack_level_get()
|
||||
{
|
||||
return FrameBufferStack.top;
|
||||
}
|
||||
|
||||
#undef FRAMEBUFFER_STACK_DEPTH
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUOffScreen
|
||||
*
|
||||
* Container that holds a frame-buffer and its textures.
|
||||
* Might be bound to multiple contexts.
|
||||
* \{ */
|
||||
|
||||
struct GPUOffScreen {
|
||||
constexpr static int MAX_CTX_FB_LEN = 3;
|
||||
|
||||
struct {
|
||||
Context *ctx;
|
||||
gpu::FrameBuffer *fb;
|
||||
} framebuffers[MAX_CTX_FB_LEN];
|
||||
|
||||
gpu::Texture *color;
|
||||
gpu::Texture *depth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the correct frame-buffer for the current context.
|
||||
*/
|
||||
static gpu::FrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs)
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
BLI_assert(ctx);
|
||||
|
||||
for (auto &framebuffer : ofs->framebuffers) {
|
||||
if (framebuffer.fb == nullptr) {
|
||||
framebuffer.ctx = ctx;
|
||||
GPU_framebuffer_ensure_config(&framebuffer.fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(ofs->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(ofs->color),
|
||||
});
|
||||
}
|
||||
|
||||
if (framebuffer.ctx == ctx) {
|
||||
return framebuffer.fb;
|
||||
}
|
||||
}
|
||||
|
||||
/* List is full, this should never happen or
|
||||
* it might just slow things down if it happens
|
||||
* regularly. In this case we just empty the list
|
||||
* and start over. This is most likely never going
|
||||
* to happen under normal usage. */
|
||||
BLI_assert(0);
|
||||
printf(
|
||||
"Warning: GPUOffscreen used in more than 3 GPUContext. "
|
||||
"This may create performance drop.\n");
|
||||
|
||||
for (auto &framebuffer : ofs->framebuffers) {
|
||||
GPU_framebuffer_free(framebuffer.fb);
|
||||
framebuffer.fb = nullptr;
|
||||
}
|
||||
|
||||
return gpu_offscreen_fb_get(ofs);
|
||||
}
|
||||
|
||||
GPUOffScreen *GPU_offscreen_create(int width,
|
||||
int height,
|
||||
bool with_depth_buffer,
|
||||
gpu::TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
bool clear,
|
||||
char err_out[256])
|
||||
{
|
||||
GPUOffScreen *ofs = MEM_new_zeroed<GPUOffScreen>(__func__);
|
||||
|
||||
/* Sometimes areas can have 0 height or width and this will
|
||||
* create a 1D texture which we don't want. */
|
||||
height = max_ii(1, height);
|
||||
width = max_ii(1, width);
|
||||
|
||||
/* Always add GPU_TEXTURE_USAGE_ATTACHMENT for convenience. */
|
||||
usage |= GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
|
||||
ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, usage, nullptr);
|
||||
|
||||
if (with_depth_buffer) {
|
||||
/* Format view flag is needed by Workbench Volumes to read the stencil view. */
|
||||
eGPUTextureUsage depth_usage = usage | GPU_TEXTURE_USAGE_FORMAT_VIEW;
|
||||
ofs->depth = GPU_texture_create_2d("ofs_depth",
|
||||
width,
|
||||
height,
|
||||
1,
|
||||
gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8,
|
||||
depth_usage,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
if ((with_depth_buffer && !ofs->depth) || !ofs->color) {
|
||||
const char error[] = "gpu::Texture: Texture allocation failed.";
|
||||
if (err_out) {
|
||||
BLI_strncpy(err_out, error, 256);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s", error);
|
||||
}
|
||||
GPU_offscreen_free(ofs);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *fb = gpu_offscreen_fb_get(ofs);
|
||||
|
||||
/* check validity at the very end! */
|
||||
if (!GPU_framebuffer_check_valid(fb, err_out)) {
|
||||
GPU_offscreen_free(ofs);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
GPU_framebuffer_bind(fb);
|
||||
if (with_depth_buffer) {
|
||||
GPU_framebuffer_clear_color_depth(fb, {0.0, 0.0, 0.0, 0.0}, 0.0f);
|
||||
}
|
||||
else {
|
||||
GPU_framebuffer_clear_color(fb, {0.0, 0.0, 0.0, 0.0});
|
||||
}
|
||||
}
|
||||
|
||||
GPU_framebuffer_restore();
|
||||
return ofs;
|
||||
}
|
||||
|
||||
void GPU_offscreen_free(GPUOffScreen *offscreen)
|
||||
{
|
||||
for (auto &framebuffer : offscreen->framebuffers) {
|
||||
if (framebuffer.fb) {
|
||||
GPU_framebuffer_free(framebuffer.fb);
|
||||
}
|
||||
}
|
||||
if (offscreen->color) {
|
||||
GPU_texture_free(offscreen->color);
|
||||
}
|
||||
if (offscreen->depth) {
|
||||
GPU_texture_free(offscreen->depth);
|
||||
}
|
||||
|
||||
MEM_delete(offscreen);
|
||||
}
|
||||
|
||||
void GPU_offscreen_bind(GPUOffScreen *offscreen, bool save)
|
||||
{
|
||||
if (save) {
|
||||
gpu::FrameBuffer *fb = GPU_framebuffer_active_get();
|
||||
GPU_framebuffer_push(fb);
|
||||
}
|
||||
gpu_offscreen_fb_get(offscreen)->bind(false);
|
||||
}
|
||||
|
||||
void GPU_offscreen_unbind(GPUOffScreen * /*offscreen*/, bool restore)
|
||||
{
|
||||
gpu::FrameBuffer *fb = nullptr;
|
||||
if (restore) {
|
||||
fb = GPU_framebuffer_pop();
|
||||
}
|
||||
|
||||
if (fb) {
|
||||
GPU_framebuffer_bind(fb);
|
||||
}
|
||||
else {
|
||||
GPU_framebuffer_restore();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_offscreen_draw_to_screen(GPUOffScreen *offscreen, int x, int y)
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
FrameBuffer *ofs_fb = gpu_offscreen_fb_get(offscreen);
|
||||
ofs_fb->blit_to(GPU_COLOR_BIT, 0, ctx->active_fb, 0, x, y);
|
||||
}
|
||||
|
||||
void GPU_offscreen_read_color_region(
|
||||
GPUOffScreen *offscreen, eGPUDataFormat format, int x, int y, int w, int h, void *r_data)
|
||||
{
|
||||
BLI_assert(ELEM(format, GPU_DATA_UBYTE, GPU_DATA_FLOAT));
|
||||
BLI_assert(x >= 0 && y >= 0 && w > 0 && h > 0);
|
||||
BLI_assert(x + w <= GPU_texture_width(offscreen->color));
|
||||
BLI_assert(y + h <= GPU_texture_height(offscreen->color));
|
||||
|
||||
gpu::FrameBuffer *ofs_fb = gpu_offscreen_fb_get(offscreen);
|
||||
GPU_framebuffer_read_color(ofs_fb, x, y, w, h, 4, 0, format, r_data);
|
||||
}
|
||||
|
||||
void GPU_offscreen_read_color(GPUOffScreen *offscreen, eGPUDataFormat format, void *r_data)
|
||||
{
|
||||
BLI_assert(ELEM(format, GPU_DATA_UBYTE, GPU_DATA_FLOAT));
|
||||
|
||||
const int w = GPU_texture_width(offscreen->color);
|
||||
const int h = GPU_texture_height(offscreen->color);
|
||||
|
||||
GPU_offscreen_read_color_region(offscreen, format, 0, 0, w, h, r_data);
|
||||
}
|
||||
|
||||
int GPU_offscreen_width(const GPUOffScreen *offscreen)
|
||||
{
|
||||
return GPU_texture_width(offscreen->color);
|
||||
}
|
||||
|
||||
int GPU_offscreen_height(const GPUOffScreen *offscreen)
|
||||
{
|
||||
return GPU_texture_height(offscreen->color);
|
||||
}
|
||||
|
||||
gpu::Texture *GPU_offscreen_color_texture(const GPUOffScreen *offscreen)
|
||||
{
|
||||
return offscreen->color;
|
||||
}
|
||||
|
||||
gpu::TextureFormat GPU_offscreen_format(const GPUOffScreen *offscreen)
|
||||
{
|
||||
return GPU_texture_format(offscreen->color);
|
||||
}
|
||||
|
||||
void GPU_offscreen_viewport_data_get(GPUOffScreen *offscreen,
|
||||
gpu::FrameBuffer **r_fb,
|
||||
gpu::Texture **r_color,
|
||||
gpu::Texture **r_depth)
|
||||
{
|
||||
*r_fb = gpu_offscreen_fb_get(offscreen);
|
||||
*r_color = offscreen->color;
|
||||
*r_depth = offscreen->depth;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,281 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Private frame buffer API.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
enum GPUAttachmentType : int {
|
||||
GPU_FB_DEPTH_ATTACHMENT = 0,
|
||||
GPU_FB_DEPTH_STENCIL_ATTACHMENT,
|
||||
GPU_FB_COLOR_ATTACHMENT0,
|
||||
GPU_FB_COLOR_ATTACHMENT1,
|
||||
GPU_FB_COLOR_ATTACHMENT2,
|
||||
GPU_FB_COLOR_ATTACHMENT3,
|
||||
GPU_FB_COLOR_ATTACHMENT4,
|
||||
GPU_FB_COLOR_ATTACHMENT5,
|
||||
GPU_FB_COLOR_ATTACHMENT6,
|
||||
GPU_FB_COLOR_ATTACHMENT7,
|
||||
/* Number of maximum output slots. */
|
||||
/* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to
|
||||
* the maximum number of COLOR attachments specified by glDrawBuffers. */
|
||||
GPU_FB_MAX_ATTACHMENT,
|
||||
|
||||
};
|
||||
|
||||
#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHMENT - GPU_FB_COLOR_ATTACHMENT0)
|
||||
|
||||
constexpr GPUAttachmentType operator-(GPUAttachmentType a, int b)
|
||||
{
|
||||
return static_cast<GPUAttachmentType>(int(a) - b);
|
||||
}
|
||||
|
||||
constexpr GPUAttachmentType operator+(GPUAttachmentType a, int b)
|
||||
{
|
||||
return static_cast<GPUAttachmentType>(int(a) + b);
|
||||
}
|
||||
|
||||
inline GPUAttachmentType &operator++(GPUAttachmentType &a)
|
||||
{
|
||||
a = a + 1;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline GPUAttachmentType &operator--(GPUAttachmentType &a)
|
||||
{
|
||||
a = a - 1;
|
||||
return a;
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define DEBUG_NAME_LEN 64
|
||||
#else
|
||||
# define DEBUG_NAME_LEN 16
|
||||
#endif
|
||||
|
||||
class FrameBuffer {
|
||||
protected:
|
||||
/** Set of texture attachments to render to. DEPTH and DEPTH_STENCIL are mutually exclusive. */
|
||||
GPUAttachment attachments_[GPU_FB_MAX_ATTACHMENT];
|
||||
/** Is true if internal representation need to be updated. */
|
||||
bool dirty_attachments_ = true;
|
||||
/** Size of attachment textures. */
|
||||
int width_ = 0, height_ = 0;
|
||||
/** Debug name. */
|
||||
char name_[DEBUG_NAME_LEN];
|
||||
/** Frame-buffer state. */
|
||||
int viewport_[GPU_MAX_VIEWPORTS][4] = {{0}};
|
||||
int scissor_[4] = {0};
|
||||
bool multi_viewport_ = false;
|
||||
bool scissor_test_ = false;
|
||||
bool dirty_state_ = true;
|
||||
/* Flag specifying the current bind operation should use explicit load-store state. */
|
||||
bool use_explicit_load_store_ = false;
|
||||
/** Bit-set indicating the color attachments slots in use. */
|
||||
uint16_t color_attachments_bits_ = 0;
|
||||
|
||||
public:
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
/**
|
||||
* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
|
||||
* Points to #BPyGPUFrameBuffer.fb
|
||||
*/
|
||||
void **py_ref = nullptr;
|
||||
#endif
|
||||
|
||||
FrameBuffer(const char *name);
|
||||
virtual ~FrameBuffer();
|
||||
|
||||
virtual void bind(bool enabled_srgb) = 0;
|
||||
virtual bool check(char err_out[256]) = 0;
|
||||
virtual void clear(GPUFrameBufferBits buffers,
|
||||
const double4 clear_color,
|
||||
float clear_depth,
|
||||
uint clear_stencil) = 0;
|
||||
virtual void clear_multi(Span<double4> clear_cols) = 0;
|
||||
virtual void clear_attachment(GPUAttachmentType type, const double4 clear_value) = 0;
|
||||
|
||||
virtual void attachment_set_loadstore_op(GPUAttachmentType type, GPULoadStore ls) = 0;
|
||||
|
||||
virtual void read(GPUFrameBufferBits planes,
|
||||
eGPUDataFormat format,
|
||||
const int area[4],
|
||||
int channel_len,
|
||||
int slot,
|
||||
void *r_data) = 0;
|
||||
|
||||
virtual void blit_to(GPUFrameBufferBits planes,
|
||||
int src_slot,
|
||||
FrameBuffer *dst,
|
||||
int dst_slot,
|
||||
int dst_offset_x,
|
||||
int dst_offset_y) = 0;
|
||||
|
||||
protected:
|
||||
virtual void subpass_transition_impl(const GPUAttachmentState depth_attachment_state,
|
||||
Span<GPUAttachmentState> color_attachment_states) = 0;
|
||||
|
||||
void set_color_attachment_bit(GPUAttachmentType type, bool value)
|
||||
{
|
||||
if (type >= GPU_FB_COLOR_ATTACHMENT0) {
|
||||
int color_index = type - GPU_FB_COLOR_ATTACHMENT0;
|
||||
SET_FLAG_FROM_TEST(color_attachments_bits_, value, 1u << color_index);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void subpass_transition(const GPUAttachmentState depth_attachment_state,
|
||||
Span<GPUAttachmentState> color_attachment_states);
|
||||
|
||||
void load_store_config_array(const GPULoadStore *load_store_actions, uint actions_len);
|
||||
|
||||
void attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment);
|
||||
void attachment_remove(GPUAttachmentType type);
|
||||
|
||||
uint get_bits_per_pixel();
|
||||
|
||||
/* Sets the size after creation. */
|
||||
void size_set(int width, int height)
|
||||
{
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
dirty_state_ = true;
|
||||
}
|
||||
|
||||
/** \brief Get the size of the framebuffer. */
|
||||
int2 size_get() const
|
||||
{
|
||||
return int2(width_, height_);
|
||||
}
|
||||
|
||||
/* Sets the size for frame-buffer with no attachments. */
|
||||
void default_size_set(int width, int height)
|
||||
{
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
dirty_attachments_ = true;
|
||||
dirty_state_ = true;
|
||||
}
|
||||
|
||||
void viewport_set(const int viewport[4])
|
||||
{
|
||||
if (!equals_v4v4_int(viewport_[0], viewport)) {
|
||||
copy_v4_v4_int(viewport_[0], viewport);
|
||||
dirty_state_ = true;
|
||||
}
|
||||
multi_viewport_ = false;
|
||||
}
|
||||
|
||||
void viewport_multi_set(const int viewports[GPU_MAX_VIEWPORTS][4])
|
||||
{
|
||||
for (size_t i = 0; i < GPU_MAX_VIEWPORTS; i++) {
|
||||
if (!equals_v4v4_int(viewport_[i], viewports[i])) {
|
||||
copy_v4_v4_int(viewport_[i], viewports[i]);
|
||||
dirty_state_ = true;
|
||||
}
|
||||
}
|
||||
multi_viewport_ = true;
|
||||
}
|
||||
|
||||
void scissor_set(const int scissor[4])
|
||||
{
|
||||
if (!equals_v4v4_int(scissor_, scissor)) {
|
||||
copy_v4_v4_int(scissor_, scissor);
|
||||
dirty_state_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void scissor_test_set(bool test)
|
||||
{
|
||||
scissor_test_ = test;
|
||||
dirty_state_ = true;
|
||||
}
|
||||
|
||||
void viewport_get(int r_viewport[4]) const
|
||||
{
|
||||
copy_v4_v4_int(r_viewport, viewport_[0]);
|
||||
}
|
||||
|
||||
void scissor_get(int r_scissor[4]) const
|
||||
{
|
||||
copy_v4_v4_int(r_scissor, scissor_);
|
||||
}
|
||||
|
||||
bool scissor_test_get() const
|
||||
{
|
||||
return scissor_test_;
|
||||
}
|
||||
|
||||
void viewport_reset()
|
||||
{
|
||||
int viewport_rect[4] = {0, 0, width_, height_};
|
||||
viewport_set(viewport_rect);
|
||||
}
|
||||
|
||||
void scissor_reset()
|
||||
{
|
||||
int scissor_rect[4] = {0, 0, width_, height_};
|
||||
scissor_set(scissor_rect);
|
||||
}
|
||||
|
||||
const GPUAttachment &depth_attachment() const
|
||||
{
|
||||
if (attachments_[GPU_FB_DEPTH_ATTACHMENT].tex) {
|
||||
return attachments_[GPU_FB_DEPTH_ATTACHMENT];
|
||||
}
|
||||
return attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT];
|
||||
}
|
||||
|
||||
gpu::Texture *depth_tex() const
|
||||
{
|
||||
return depth_attachment().tex;
|
||||
};
|
||||
|
||||
gpu::Texture *color_tex(int slot) const
|
||||
{
|
||||
return attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex;
|
||||
};
|
||||
|
||||
const char *name_get() const
|
||||
{
|
||||
return name_;
|
||||
};
|
||||
|
||||
void set_use_explicit_loadstore(bool use_explicit_loadstore)
|
||||
{
|
||||
use_explicit_load_store_ = use_explicit_loadstore;
|
||||
}
|
||||
|
||||
bool get_use_explicit_loadstore() const
|
||||
{
|
||||
return use_explicit_load_store_;
|
||||
}
|
||||
|
||||
uint16_t get_color_attachments_bitset()
|
||||
{
|
||||
return color_attachments_bits_;
|
||||
}
|
||||
};
|
||||
|
||||
#undef DEBUG_NAME_LEN
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
772
blender-5.2.0/source/blender/gpu/intern/gpu_immediate.cc
Normal file
772
blender-5.2.0/source/blender/gpu/intern/gpu_immediate.cc
Normal file
@@ -0,0 +1,772 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style opengl immediate mode drawing.
|
||||
*/
|
||||
|
||||
#ifndef GPU_STANDALONE
|
||||
# include "UI_resources.hh"
|
||||
#endif
|
||||
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_matrix.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_uniform_buffer.hh"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_immediate_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
#include "gpu_vertex_format_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
static thread_local Immediate *imm = nullptr;
|
||||
|
||||
void immActivate()
|
||||
{
|
||||
imm = Context::get()->imm;
|
||||
}
|
||||
|
||||
void immDeactivate()
|
||||
{
|
||||
imm = nullptr;
|
||||
}
|
||||
|
||||
GPUVertFormat *immVertexFormat()
|
||||
{
|
||||
GPU_vertformat_clear(&imm->vertex_format);
|
||||
return &imm->vertex_format;
|
||||
}
|
||||
|
||||
void immBindShader(gpu::Shader *shader)
|
||||
{
|
||||
BLI_assert(imm->shader == nullptr);
|
||||
|
||||
imm->shader = shader;
|
||||
imm->builtin_shader_bound = std::nullopt;
|
||||
|
||||
if (!imm->vertex_format.packed) {
|
||||
VertexFormat_pack(&imm->vertex_format);
|
||||
imm->enabled_attr_bits = 0xFFFFu & ~(0xFFFFu << imm->vertex_format.attr_len);
|
||||
}
|
||||
|
||||
GPU_shader_bind(shader);
|
||||
GPU_matrix_bind(shader);
|
||||
}
|
||||
|
||||
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
|
||||
{
|
||||
gpu::Shader *shader = GPU_shader_get_builtin_shader(shader_id);
|
||||
immBindShader(shader);
|
||||
imm->builtin_shader_bound = shader_id;
|
||||
}
|
||||
|
||||
void immUnbindProgram()
|
||||
{
|
||||
BLI_assert(imm->shader != nullptr);
|
||||
|
||||
GPU_shader_unbind();
|
||||
imm->shader = nullptr;
|
||||
}
|
||||
|
||||
bool immIsShaderBound()
|
||||
{
|
||||
return imm->shader != nullptr;
|
||||
}
|
||||
|
||||
gpu::Shader *immGetShader()
|
||||
{
|
||||
return imm->shader;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType prim_type)
|
||||
{
|
||||
/* does vertex_len make sense for this primitive type? */
|
||||
if (vertex_len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (prim_type) {
|
||||
case GPU_PRIM_POINTS:
|
||||
return true;
|
||||
case GPU_PRIM_LINES:
|
||||
return vertex_len % 2 == 0;
|
||||
case GPU_PRIM_LINE_STRIP:
|
||||
case GPU_PRIM_LINE_LOOP:
|
||||
return vertex_len >= 2;
|
||||
case GPU_PRIM_LINE_STRIP_ADJ:
|
||||
return vertex_len >= 4;
|
||||
case GPU_PRIM_TRIS:
|
||||
return vertex_len % 3 == 0;
|
||||
case GPU_PRIM_TRI_STRIP:
|
||||
case GPU_PRIM_TRI_FAN:
|
||||
return vertex_len >= 3;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Wide line workaround
|
||||
*
|
||||
* Some systems do not support wide lines.
|
||||
* We workaround this by using specialized shaders.
|
||||
* \{ */
|
||||
|
||||
static void wide_line_workaround_start(GPUPrimType prim_type)
|
||||
{
|
||||
if (!ELEM(prim_type, GPU_PRIM_LINES, GPU_PRIM_LINE_STRIP, GPU_PRIM_LINE_LOOP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
float line_width = GPU_line_width_get();
|
||||
|
||||
if (line_width == 1.0f && !GPU_line_smooth_get()) {
|
||||
/* No need to change the shader. */
|
||||
return;
|
||||
}
|
||||
if (!imm->builtin_shader_bound) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPUBuiltinShader polyline_sh;
|
||||
switch (*imm->builtin_shader_bound) {
|
||||
case GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR:
|
||||
polyline_sh = GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR;
|
||||
break;
|
||||
case GPU_SHADER_3D_UNIFORM_COLOR:
|
||||
polyline_sh = GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR;
|
||||
break;
|
||||
case GPU_SHADER_3D_FLAT_COLOR:
|
||||
polyline_sh = GPU_SHADER_3D_POLYLINE_FLAT_COLOR;
|
||||
break;
|
||||
case GPU_SHADER_3D_SMOOTH_COLOR:
|
||||
polyline_sh = GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR;
|
||||
break;
|
||||
default:
|
||||
/* Cannot replace the current shader with a polyline shader. */
|
||||
return;
|
||||
}
|
||||
|
||||
imm->prev_builtin_shader = imm->builtin_shader_bound;
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
/* TODO(fclem): Don't use geometry shader and use quad instancing with double load. */
|
||||
// GPU_vertformat_multiload_enable(imm->vertex_format, 2);
|
||||
|
||||
immBindBuiltinProgram(polyline_sh);
|
||||
|
||||
float viewport[4];
|
||||
GPU_viewport_size_get_f(viewport);
|
||||
immUniform2fv("viewportSize", &viewport[2]);
|
||||
immUniform1f("lineWidth", line_width);
|
||||
|
||||
if (GPU_blend_get() == GPU_BLEND_NONE) {
|
||||
/* Disable line smoothing when blending is disabled (see #81827). */
|
||||
immUniform1i("lineSmooth", 0);
|
||||
}
|
||||
|
||||
if (ELEM(polyline_sh,
|
||||
GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR,
|
||||
GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR))
|
||||
{
|
||||
immUniformColor4fv(imm->uniform_color);
|
||||
}
|
||||
}
|
||||
|
||||
static void wide_line_workaround_end()
|
||||
{
|
||||
if (imm->prev_builtin_shader) {
|
||||
if (GPU_blend_get() == GPU_BLEND_NONE) {
|
||||
/* Restore default. */
|
||||
immUniform1i("lineSmooth", 1);
|
||||
}
|
||||
immUnbindProgram();
|
||||
|
||||
immBindBuiltinProgram(*imm->prev_builtin_shader);
|
||||
imm->prev_builtin_shader = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void immBegin(GPUPrimType prim_type, uint vertex_len)
|
||||
{
|
||||
BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */
|
||||
BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
|
||||
|
||||
wide_line_workaround_start(prim_type);
|
||||
|
||||
imm->prim_type = prim_type;
|
||||
imm->vertex_len = vertex_len;
|
||||
imm->vertex_idx = 0;
|
||||
imm->unassigned_attr_bits = imm->enabled_attr_bits;
|
||||
|
||||
imm->vertex_data = imm->begin();
|
||||
}
|
||||
|
||||
void immBeginAtMost(GPUPrimType prim_type, uint vertex_len)
|
||||
{
|
||||
BLI_assert(vertex_len > 0);
|
||||
imm->strict_vertex_len = false;
|
||||
immBegin(prim_type, vertex_len);
|
||||
}
|
||||
|
||||
gpu::Batch *immBeginBatch(GPUPrimType prim_type, uint vertex_len)
|
||||
{
|
||||
BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */
|
||||
BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
|
||||
|
||||
imm->prim_type = prim_type;
|
||||
imm->vertex_len = vertex_len;
|
||||
imm->vertex_idx = 0;
|
||||
imm->unassigned_attr_bits = imm->enabled_attr_bits;
|
||||
|
||||
VertBuf *verts = GPU_vertbuf_create_with_format(imm->vertex_format);
|
||||
GPU_vertbuf_data_alloc(*verts, vertex_len);
|
||||
|
||||
imm->vertex_data = verts->data<uchar>().data();
|
||||
|
||||
imm->batch = GPU_batch_create_ex(prim_type, verts, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
imm->batch->flag |= GPU_BATCH_BUILDING;
|
||||
|
||||
return imm->batch;
|
||||
}
|
||||
|
||||
gpu::Batch *immBeginBatchAtMost(GPUPrimType prim_type, uint vertex_len)
|
||||
{
|
||||
BLI_assert(vertex_len > 0);
|
||||
imm->strict_vertex_len = false;
|
||||
return immBeginBatch(prim_type, vertex_len);
|
||||
}
|
||||
|
||||
void immEnd()
|
||||
{
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* Make sure we're between a Begin/End pair. */
|
||||
BLI_assert(imm->vertex_data || imm->batch);
|
||||
|
||||
if (imm->strict_vertex_len) {
|
||||
BLI_assert(imm->vertex_idx == imm->vertex_len); /* With all vertices defined. */
|
||||
}
|
||||
else {
|
||||
BLI_assert(imm->vertex_idx <= imm->vertex_len);
|
||||
BLI_assert(imm->vertex_idx == 0 ||
|
||||
vertex_count_makes_sense_for_primitive(imm->vertex_idx, imm->prim_type));
|
||||
}
|
||||
|
||||
if (imm->batch) {
|
||||
if (imm->vertex_idx < imm->vertex_len) {
|
||||
GPU_vertbuf_data_resize(*imm->batch->verts[0], imm->vertex_idx);
|
||||
/* TODO: resize only if vertex count is much smaller */
|
||||
}
|
||||
GPU_batch_set_shader(imm->batch, imm->shader);
|
||||
imm->batch->flag &= ~GPU_BATCH_BUILDING;
|
||||
imm->batch = nullptr; /* don't free, batch belongs to caller */
|
||||
}
|
||||
else {
|
||||
Context::get()->assert_framebuffer_shader_compatibility(imm->shader);
|
||||
imm->end();
|
||||
}
|
||||
|
||||
/* Prepare for next immBegin. */
|
||||
imm->prim_type = GPU_PRIM_NONE;
|
||||
imm->strict_vertex_len = true;
|
||||
imm->vertex_data = nullptr;
|
||||
|
||||
wide_line_workaround_end();
|
||||
}
|
||||
|
||||
void Immediate::polyline_draw_workaround(uint64_t offset)
|
||||
{
|
||||
/* Check compatible input primitive. */
|
||||
BLI_assert(ELEM(imm->prim_type, GPU_PRIM_LINES, GPU_PRIM_LINE_STRIP, GPU_PRIM_LINE_LOOP));
|
||||
|
||||
Batch *tri_batch = Context::get()->procedural_triangles_batch_get();
|
||||
GPU_batch_set_shader(tri_batch, imm->shader);
|
||||
|
||||
BLI_assert(offset % 4 == 0);
|
||||
|
||||
/* Setup primitive and index buffer. */
|
||||
int stride = (imm->prim_type == GPU_PRIM_LINES) ? 2 : 1;
|
||||
int data[3] = {stride, int(imm->vertex_idx), int(offset / 4)};
|
||||
GPU_shader_uniform_3iv(imm->shader, "gpu_vert_stride_count_offset", data);
|
||||
GPU_shader_uniform_1b(imm->shader, "gpu_index_no_buffer", true);
|
||||
|
||||
{
|
||||
/* Setup attributes metadata uniforms. */
|
||||
const GPUVertFormat &format = imm->vertex_format;
|
||||
/* Only support 4byte aligned formats. */
|
||||
BLI_assert((format.stride % 4) == 0);
|
||||
BLI_assert(format.attr_len > 0);
|
||||
|
||||
int pos_attr_id = -1;
|
||||
int col_attr_id = -1;
|
||||
|
||||
for (uint a_idx = 0; a_idx < format.attr_len; a_idx++) {
|
||||
const GPUVertAttr *a = &format.attrs[a_idx];
|
||||
const char *name = GPU_vertformat_attr_name_get(&format, a, 0);
|
||||
if (pos_attr_id == -1 && StringRefNull(name) == "pos") {
|
||||
int descriptor[2] = {int(format.stride) / 4, int(a->offset) / 4};
|
||||
const bool fetch_int = false;
|
||||
BLI_assert(is_fetch_float(a->type.format) || fetch_int);
|
||||
BLI_assert_msg((a->offset % 4) == 0, "Only support 4byte aligned attributes");
|
||||
GPU_shader_uniform_2iv(imm->shader, "gpu_attr_0", descriptor);
|
||||
GPU_shader_uniform_1i(imm->shader, "gpu_attr_0_len", a->type.comp_len());
|
||||
GPU_shader_uniform_1b(imm->shader, "gpu_attr_0_fetch_int", fetch_int);
|
||||
pos_attr_id = a_idx;
|
||||
}
|
||||
else if (col_attr_id == -1 && StringRefNull(name) == "color") {
|
||||
int descriptor[2] = {int(format.stride) / 4, int(a->offset) / 4};
|
||||
/* Maybe we can relax this if needed. */
|
||||
BLI_assert_msg(ELEM(a->type.format,
|
||||
VertAttrType::SFLOAT_32,
|
||||
VertAttrType::SFLOAT_32_32,
|
||||
VertAttrType::SFLOAT_32_32_32,
|
||||
VertAttrType::SFLOAT_32_32_32_32,
|
||||
VertAttrType::UNORM_8_8_8_8),
|
||||
"Only support float attributes or uchar4");
|
||||
const bool fetch_unorm8 = a->type.format == VertAttrType::UNORM_8_8_8_8;
|
||||
BLI_assert_msg((a->offset % 4) == 0, "Only support 4byte aligned attributes");
|
||||
GPU_shader_uniform_2iv(imm->shader, "gpu_attr_1", descriptor);
|
||||
GPU_shader_uniform_1i(imm->shader, "gpu_attr_1_len", a->type.comp_len());
|
||||
GPU_shader_uniform_1i(imm->shader, "gpu_attr_1_fetch_unorm8", fetch_unorm8);
|
||||
col_attr_id = a_idx;
|
||||
}
|
||||
if (pos_attr_id != -1 && col_attr_id != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(pos_attr_id != -1);
|
||||
/* Could check for color attribute but we need to know which variant of the polyline shader is
|
||||
* the one we are rendering with. */
|
||||
// BLI_assert(pos_attr_id != -1);
|
||||
}
|
||||
|
||||
IndexRange range = GPU_batch_draw_expanded_parameter_get(
|
||||
imm->prim_type, GPU_PRIM_TRIS, imm->vertex_idx, 0, 2);
|
||||
GPU_batch_draw_advanced(tri_batch, range.start(), range.size(), 0, 0);
|
||||
}
|
||||
|
||||
static void setAttrValueBit(uint attr_id)
|
||||
{
|
||||
uint16_t mask = 1 << attr_id;
|
||||
BLI_assert(imm->unassigned_attr_bits & mask); /* not already set */
|
||||
imm->unassigned_attr_bits &= ~mask;
|
||||
}
|
||||
|
||||
/* --- generic attribute functions --- */
|
||||
|
||||
void immAttr1f(uint attr_id, float x)
|
||||
{
|
||||
GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(ELEM(attr->type.format, VertAttrType::SFLOAT_32));
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
|
||||
float *data = reinterpret_cast<float *>(imm->vertex_data + attr->offset);
|
||||
// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
|
||||
|
||||
data[0] = x;
|
||||
}
|
||||
|
||||
void immAttr2f(uint attr_id, float x, float y)
|
||||
{
|
||||
GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(ELEM(attr->type.format, VertAttrType::SFLOAT_32_32));
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
|
||||
float *data = reinterpret_cast<float *>(imm->vertex_data + attr->offset);
|
||||
// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
|
||||
|
||||
data[0] = x;
|
||||
data[1] = y;
|
||||
}
|
||||
|
||||
void immAttr3f(uint attr_id, float x, float y, float z)
|
||||
{
|
||||
GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(ELEM(attr->type.format, VertAttrType::SFLOAT_32_32_32));
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
|
||||
float *data = reinterpret_cast<float *>(imm->vertex_data + attr->offset);
|
||||
// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
|
||||
|
||||
data[0] = x;
|
||||
data[1] = y;
|
||||
data[2] = z;
|
||||
}
|
||||
|
||||
void immAttr4f(uint attr_id, float x, float y, float z, float w)
|
||||
{
|
||||
GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(ELEM(attr->type.format, VertAttrType::SFLOAT_32_32_32_32));
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
|
||||
float *data = reinterpret_cast<float *>(imm->vertex_data + attr->offset);
|
||||
// printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
|
||||
|
||||
data[0] = x;
|
||||
data[1] = y;
|
||||
data[2] = z;
|
||||
data[3] = w;
|
||||
}
|
||||
|
||||
void immAttr1u(uint attr_id, uint x)
|
||||
{
|
||||
GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(ELEM(attr->type.format, VertAttrType::UINT_32));
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
|
||||
uint *data = reinterpret_cast<uint *>(imm->vertex_data + attr->offset);
|
||||
|
||||
data[0] = x;
|
||||
}
|
||||
|
||||
void immAttr2i(uint attr_id, int x, int y)
|
||||
{
|
||||
GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(ELEM(attr->type.format, VertAttrType::SINT_32_32));
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
|
||||
int *data = reinterpret_cast<int *>(imm->vertex_data + attr->offset);
|
||||
|
||||
data[0] = x;
|
||||
data[1] = y;
|
||||
}
|
||||
|
||||
void immAttr2fv(uint attr_id, const float data[2])
|
||||
{
|
||||
immAttr2f(attr_id, data[0], data[1]);
|
||||
}
|
||||
|
||||
void immAttr3fv(uint attr_id, const float data[3])
|
||||
{
|
||||
immAttr3f(attr_id, data[0], data[1], data[2]);
|
||||
}
|
||||
|
||||
void immAttr4fv(uint attr_id, const float data[4])
|
||||
{
|
||||
immAttr4f(attr_id, data[0], data[1], data[2], data[3]);
|
||||
}
|
||||
|
||||
void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a)
|
||||
{
|
||||
GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id];
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(ELEM(attr->type.format, VertAttrType::UINT_8_8_8_8, VertAttrType::UNORM_8_8_8_8));
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
|
||||
uchar *data = imm->vertex_data + attr->offset;
|
||||
// printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data);
|
||||
|
||||
data[0] = r;
|
||||
data[1] = g;
|
||||
data[2] = b;
|
||||
data[3] = a;
|
||||
}
|
||||
|
||||
void immAttr4ubv(uint attr_id, const uchar data[4])
|
||||
{
|
||||
immAttr4ub(attr_id, data[0], data[1], data[2], data[3]);
|
||||
}
|
||||
|
||||
void immAttrSkip(uint attr_id)
|
||||
{
|
||||
BLI_assert(attr_id < imm->vertex_format.attr_len);
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
setAttrValueBit(attr_id);
|
||||
}
|
||||
|
||||
static void immEndVertex() /* and move on to the next vertex */
|
||||
{
|
||||
BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
BLI_assert(imm->vertex_idx < imm->vertex_len);
|
||||
|
||||
/* Have all attributes been assigned values?
|
||||
* If not, copy value from previous vertex. */
|
||||
if (imm->unassigned_attr_bits) {
|
||||
BLI_assert(imm->vertex_idx > 0); /* first vertex must have all attributes specified */
|
||||
for (uint a_idx = 0; a_idx < imm->vertex_format.attr_len; a_idx++) {
|
||||
if ((imm->unassigned_attr_bits >> a_idx) & 1) {
|
||||
const GPUVertAttr *a = &imm->vertex_format.attrs[a_idx];
|
||||
|
||||
#if 0
|
||||
printf("copying %s from vertex %u to %u\n", a->name, imm->vertex_idx - 1, imm->vertex_idx);
|
||||
#endif
|
||||
|
||||
uchar *data = imm->vertex_data + a->offset;
|
||||
memcpy(data, data - imm->vertex_format.stride, a->type.size());
|
||||
/* TODO: consolidate copy of adjacent attributes */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imm->vertex_idx++;
|
||||
imm->vertex_data += imm->vertex_format.stride;
|
||||
imm->unassigned_attr_bits = imm->enabled_attr_bits;
|
||||
}
|
||||
|
||||
void immVertex2f(uint attr_id, float x, float y)
|
||||
{
|
||||
immAttr2f(attr_id, x, y);
|
||||
immEndVertex();
|
||||
}
|
||||
|
||||
void immVertex3f(uint attr_id, float x, float y, float z)
|
||||
{
|
||||
immAttr3f(attr_id, x, y, z);
|
||||
immEndVertex();
|
||||
}
|
||||
|
||||
void immVertex4f(uint attr_id, float x, float y, float z, float w)
|
||||
{
|
||||
immAttr4f(attr_id, x, y, z, w);
|
||||
immEndVertex();
|
||||
}
|
||||
|
||||
void immVertex2i(uint attr_id, int x, int y)
|
||||
{
|
||||
immAttr2i(attr_id, x, y);
|
||||
immEndVertex();
|
||||
}
|
||||
|
||||
void immVertex2fv(uint attr_id, const float data[2])
|
||||
{
|
||||
immAttr2f(attr_id, data[0], data[1]);
|
||||
immEndVertex();
|
||||
}
|
||||
|
||||
void immVertex3fv(uint attr_id, const float data[3])
|
||||
{
|
||||
immAttr3f(attr_id, data[0], data[1], data[2]);
|
||||
immEndVertex();
|
||||
}
|
||||
|
||||
void immVertex2iv(uint attr_id, const int data[2])
|
||||
{
|
||||
immAttr2i(attr_id, data[0], data[1]);
|
||||
immEndVertex();
|
||||
}
|
||||
|
||||
/* --- generic uniform functions --- */
|
||||
|
||||
void immUniform1f(const char *name, float x)
|
||||
{
|
||||
GPU_shader_uniform_1f(imm->shader, name, x);
|
||||
}
|
||||
|
||||
void immUniform2f(const char *name, float x, float y)
|
||||
{
|
||||
GPU_shader_uniform_2f(imm->shader, name, x, y);
|
||||
}
|
||||
|
||||
void immUniform2fv(const char *name, const float data[2])
|
||||
{
|
||||
GPU_shader_uniform_2fv(imm->shader, name, data);
|
||||
}
|
||||
|
||||
void immUniform3f(const char *name, float x, float y, float z)
|
||||
{
|
||||
GPU_shader_uniform_3f(imm->shader, name, x, y, z);
|
||||
}
|
||||
|
||||
void immUniform3fv(const char *name, const float data[3])
|
||||
{
|
||||
GPU_shader_uniform_3fv(imm->shader, name, data);
|
||||
}
|
||||
|
||||
void immUniform4f(const char *name, float x, float y, float z, float w)
|
||||
{
|
||||
GPU_shader_uniform_4f(imm->shader, name, x, y, z, w);
|
||||
}
|
||||
|
||||
void immUniform4fv(const char *name, const float data[4])
|
||||
{
|
||||
GPU_shader_uniform_4fv(imm->shader, name, data);
|
||||
}
|
||||
|
||||
void immUniformArray4fv(const char *name, const float *data, int count)
|
||||
{
|
||||
GPU_shader_uniform_4fv_array(
|
||||
imm->shader, name, count, reinterpret_cast<const float (*)[4]>(data));
|
||||
}
|
||||
|
||||
void immUniformMatrix4fv(const char *name, const float data[4][4])
|
||||
{
|
||||
GPU_shader_uniform_mat4(imm->shader, name, data);
|
||||
}
|
||||
|
||||
void immUniform1i(const char *name, int x)
|
||||
{
|
||||
GPU_shader_uniform_1i(imm->shader, name, x);
|
||||
}
|
||||
|
||||
void immBindTexture(const char *name, gpu::Texture *tex)
|
||||
{
|
||||
int binding = GPU_shader_get_sampler_binding(imm->shader, name);
|
||||
GPU_texture_bind(tex, binding);
|
||||
}
|
||||
|
||||
void immBindTextureSampler(const char *name, gpu::Texture *tex, GPUSamplerState state)
|
||||
{
|
||||
int binding = GPU_shader_get_sampler_binding(imm->shader, name);
|
||||
GPU_texture_bind_ex(tex, state, binding);
|
||||
}
|
||||
|
||||
void immBindUniformBuf(const char *name, gpu::UniformBuf *ubo)
|
||||
{
|
||||
int binding = GPU_shader_get_ubo_binding(imm->shader, name);
|
||||
GPU_uniformbuf_bind(ubo, binding);
|
||||
}
|
||||
|
||||
/* --- convenience functions for setting "uniform vec4 color" --- */
|
||||
|
||||
void immUniformColor4f(float r, float g, float b, float a)
|
||||
{
|
||||
int32_t uniform_loc = GPU_shader_get_builtin_uniform(imm->shader, GPU_UNIFORM_COLOR);
|
||||
BLI_assert(uniform_loc != -1);
|
||||
float data[4] = {r, g, b, a};
|
||||
GPU_shader_uniform_float_ex(imm->shader, uniform_loc, 4, 1, data);
|
||||
/* For wide Line workaround. */
|
||||
copy_v4_v4(imm->uniform_color, data);
|
||||
}
|
||||
|
||||
void immUniformColor4fv(const float rgba[4])
|
||||
{
|
||||
immUniformColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
}
|
||||
|
||||
void immUniformColor3f(float r, float g, float b)
|
||||
{
|
||||
immUniformColor4f(r, g, b, 1.0f);
|
||||
}
|
||||
|
||||
void immUniformColor3fv(const float rgb[3])
|
||||
{
|
||||
immUniformColor4f(rgb[0], rgb[1], rgb[2], 1.0f);
|
||||
}
|
||||
|
||||
void immUniformColor3fvAlpha(const float rgb[3], float a)
|
||||
{
|
||||
immUniformColor4f(rgb[0], rgb[1], rgb[2], a);
|
||||
}
|
||||
|
||||
void immUniformColor3ub(uchar r, uchar g, uchar b)
|
||||
{
|
||||
const float scale = 1.0f / 255.0f;
|
||||
immUniformColor4f(scale * r, scale * g, scale * b, 1.0f);
|
||||
}
|
||||
|
||||
void immUniformColor4ub(uchar r, uchar g, uchar b, uchar a)
|
||||
{
|
||||
const float scale = 1.0f / 255.0f;
|
||||
immUniformColor4f(scale * r, scale * g, scale * b, scale * a);
|
||||
}
|
||||
|
||||
void immUniformColor3ubv(const uchar rgb[3])
|
||||
{
|
||||
immUniformColor3ub(rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
void immUniformColor3ubvAlpha(const uchar rgb[3], uchar alpha)
|
||||
{
|
||||
immUniformColor4ub(rgb[0], rgb[1], rgb[2], alpha);
|
||||
}
|
||||
|
||||
void immUniformColor4ubv(const uchar rgba[4])
|
||||
{
|
||||
immUniformColor4ub(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
}
|
||||
|
||||
#ifndef GPU_STANDALONE
|
||||
|
||||
void immUniformThemeColor(int color_id)
|
||||
{
|
||||
float color[4];
|
||||
ui::theme::get_color_4fv(color_id, color);
|
||||
immUniformColor4fv(color);
|
||||
}
|
||||
|
||||
void immUniformThemeColorAlpha(int color_id, float a)
|
||||
{
|
||||
float color[4];
|
||||
ui::theme::get_color_3fv(color_id, color);
|
||||
color[3] = a;
|
||||
immUniformColor4fv(color);
|
||||
}
|
||||
|
||||
void immUniformThemeColor3(int color_id)
|
||||
{
|
||||
float color[3];
|
||||
ui::theme::get_color_3fv(color_id, color);
|
||||
immUniformColor3fv(color);
|
||||
}
|
||||
|
||||
void immUniformThemeColorShade(int color_id, int offset)
|
||||
{
|
||||
float color[4];
|
||||
ui::theme::get_color_shade_4fv(color_id, offset, color);
|
||||
immUniformColor4fv(color);
|
||||
}
|
||||
|
||||
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
|
||||
{
|
||||
float color[4];
|
||||
ui::theme::get_color_shade_alpha_4fv(color_id, color_offset, alpha_offset, color);
|
||||
immUniformColor4fv(color);
|
||||
}
|
||||
|
||||
void immUniformThemeColorBlendShade(int color_id1, int color_id2, float fac, int offset)
|
||||
{
|
||||
float color[4];
|
||||
ui::theme::get_color_blend_shade_4fv(color_id1, color_id2, fac, offset, color);
|
||||
immUniformColor4fv(color);
|
||||
}
|
||||
|
||||
void immUniformThemeColorBlend(int color_id1, int color_id2, float fac)
|
||||
{
|
||||
uint8_t color[3];
|
||||
ui::theme::get_color_blend_3ubv(color_id1, color_id2, fac, color);
|
||||
immUniformColor3ubv(color);
|
||||
}
|
||||
|
||||
void immThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset)
|
||||
{
|
||||
uchar col[4];
|
||||
ui::theme::get_color_shade_alpha_4ubv(colorid, coloffset, alphaoffset, col);
|
||||
immUniformColor4ub(col[0], col[1], col[2], col[3]);
|
||||
}
|
||||
|
||||
#endif /* !GPU_STANDALONE */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style opengl immediate mode drawing.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_primitive.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_vertex_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class Immediate {
|
||||
public:
|
||||
/** Pointer to the mapped buffer data for the current vertex. */
|
||||
uchar *vertex_data = nullptr;
|
||||
/** Current vertex index. */
|
||||
uint vertex_idx = 0;
|
||||
/** Length of the buffer in vertices. */
|
||||
uint vertex_len = 0;
|
||||
/** Which attributes of current vertex have not been given values? */
|
||||
uint16_t unassigned_attr_bits = 0;
|
||||
/** Attributes that needs to be set. One bit per attribute. */
|
||||
uint16_t enabled_attr_bits = 0;
|
||||
|
||||
/** Current draw call specification. */
|
||||
GPUPrimType prim_type = GPU_PRIM_NONE;
|
||||
GPUVertFormat vertex_format = {};
|
||||
gpu::Shader *shader = nullptr;
|
||||
/** Enforce strict vertex count (disabled when using #immBeginAtMost). */
|
||||
bool strict_vertex_len = true;
|
||||
|
||||
/** Batch in construction when using #immBeginBatch. */
|
||||
Batch *batch = nullptr;
|
||||
|
||||
/** Wide Line workaround. */
|
||||
|
||||
/** Previously bound shader to restore after drawing. */
|
||||
std::optional<GPUBuiltinShader> prev_builtin_shader;
|
||||
/** Builtin shader index. Used to test if the line width workaround can be done. */
|
||||
std::optional<GPUBuiltinShader> builtin_shader_bound;
|
||||
/** Uniform color: Kept here to update the wide-line shader just before #immBegin. */
|
||||
float uniform_color[4];
|
||||
|
||||
Immediate() = default;
|
||||
virtual ~Immediate() = default;
|
||||
|
||||
virtual uchar *begin() = 0;
|
||||
virtual void end() = 0;
|
||||
|
||||
/* To be called after polyline SSBO binding. */
|
||||
void polyline_draw_workaround(uint64_t offset);
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
void immActivate();
|
||||
void immDeactivate();
|
||||
|
||||
} // namespace blender
|
||||
752
blender-5.2.0/source/blender/gpu/intern/gpu_immediate_util.cc
Normal file
752
blender-5.2.0/source/blender/gpu/intern/gpu_immediate_util.cc
Normal file
@@ -0,0 +1,752 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU immediate mode drawing utilities
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_immediate.hh"
|
||||
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static const float cube_coords[8][3] = {
|
||||
{-1, -1, -1},
|
||||
{-1, -1, +1},
|
||||
{-1, +1, -1},
|
||||
{-1, +1, +1},
|
||||
{+1, -1, -1},
|
||||
{+1, -1, +1},
|
||||
{+1, +1, -1},
|
||||
{+1, +1, +1},
|
||||
};
|
||||
static const int cube_quad_index[6][4] = {
|
||||
{0, 1, 3, 2},
|
||||
{0, 2, 6, 4},
|
||||
{0, 4, 5, 1},
|
||||
{1, 5, 7, 3},
|
||||
{2, 3, 7, 6},
|
||||
{4, 6, 7, 5},
|
||||
};
|
||||
static const int cube_line_index[12][2] = {
|
||||
{0, 1},
|
||||
{0, 2},
|
||||
{0, 4},
|
||||
{1, 3},
|
||||
{1, 5},
|
||||
{2, 3},
|
||||
{2, 6},
|
||||
{3, 7},
|
||||
{4, 5},
|
||||
{4, 6},
|
||||
{5, 7},
|
||||
{6, 7},
|
||||
};
|
||||
|
||||
void immRectf(uint pos, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
immBegin(GPU_PRIM_TRI_FAN, 4);
|
||||
immVertex2f(pos, x1, y1);
|
||||
immVertex2f(pos, x2, y1);
|
||||
immVertex2f(pos, x2, y2);
|
||||
immVertex2f(pos, x1, y2);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void immRecti(uint pos, int x1, int y1, int x2, int y2)
|
||||
{
|
||||
immBegin(GPU_PRIM_TRI_FAN, 4);
|
||||
immVertex2i(pos, x1, y1);
|
||||
immVertex2i(pos, x2, y1);
|
||||
immVertex2i(pos, x2, y2);
|
||||
immVertex2i(pos, x1, y2);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void immRectf_fast(uint pos, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
immVertex2f(pos, x1, y1);
|
||||
immVertex2f(pos, x2, y1);
|
||||
immVertex2f(pos, x2, y2);
|
||||
|
||||
immVertex2f(pos, x1, y1);
|
||||
immVertex2f(pos, x2, y2);
|
||||
immVertex2f(pos, x1, y2);
|
||||
}
|
||||
|
||||
void immRectf_fast_with_color(
|
||||
uint pos, uint col, float x1, float y1, float x2, float y2, const float color[4])
|
||||
{
|
||||
immAttr4fv(col, color);
|
||||
immVertex2f(pos, x1, y1);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2f(pos, x2, y1);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2f(pos, x2, y2);
|
||||
|
||||
immAttr4fv(col, color);
|
||||
immVertex2f(pos, x1, y1);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2f(pos, x2, y2);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2f(pos, x1, y2);
|
||||
}
|
||||
|
||||
void immRecti_fast_with_color(
|
||||
uint pos, uint col, int x1, int y1, int x2, int y2, const float color[4])
|
||||
{
|
||||
immAttr4fv(col, color);
|
||||
immVertex2i(pos, x1, y1);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2i(pos, x2, y1);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2i(pos, x2, y2);
|
||||
|
||||
immAttr4fv(col, color);
|
||||
immVertex2i(pos, x1, y1);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2i(pos, x2, y2);
|
||||
immAttr4fv(col, color);
|
||||
immVertex2i(pos, x1, y2);
|
||||
}
|
||||
|
||||
void immRectf_with_texco(const uint pos, const uint tex_coord, const rctf &p, const rctf &uv)
|
||||
{
|
||||
immBegin(GPU_PRIM_TRI_FAN, 4);
|
||||
immAttr2f(tex_coord, uv.xmin, uv.ymin);
|
||||
immVertex2f(pos, p.xmin, p.ymin);
|
||||
|
||||
immAttr2f(tex_coord, uv.xmin, uv.ymax);
|
||||
immVertex2f(pos, p.xmin, p.ymax);
|
||||
|
||||
immAttr2f(tex_coord, uv.xmax, uv.ymax);
|
||||
immVertex2f(pos, p.xmax, p.ymax);
|
||||
|
||||
immAttr2f(tex_coord, uv.xmax, uv.ymin);
|
||||
immVertex2f(pos, p.xmax, p.ymin);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
#if 0 /* more complete version in case we want that */
|
||||
void immRecti_complete(int x1, int y1, int x2, int y2, const float color[4])
|
||||
{
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos = add_attr(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
immUniformColor4fv(color);
|
||||
immRectf(pos, x1, y1, x2, y2);
|
||||
immUnbindProgram();
|
||||
}
|
||||
#endif
|
||||
|
||||
void imm_cpack(uint x)
|
||||
{
|
||||
immUniformColor3ub(((x) & 0xFF), (((x) >> 8) & 0xFF), (((x) >> 16) & 0xFF));
|
||||
}
|
||||
|
||||
static void imm_draw_circle(GPUPrimType prim_type,
|
||||
const uint shdr_pos,
|
||||
float x,
|
||||
float y,
|
||||
float radius_x,
|
||||
float radius_y,
|
||||
int nsegments)
|
||||
{
|
||||
if (prim_type == GPU_PRIM_LINE_LOOP) {
|
||||
/* NOTE(Metal/AMD): For small primitives, line list more efficient than line strip.. */
|
||||
immBegin(GPU_PRIM_LINES, nsegments * 2);
|
||||
|
||||
immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f)));
|
||||
for (int i = 1; i < nsegments; i++) {
|
||||
const float angle = float(2 * M_PI) * (float(i) / float(nsegments));
|
||||
immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
|
||||
immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
|
||||
}
|
||||
immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f)));
|
||||
immEnd();
|
||||
}
|
||||
else {
|
||||
immBegin(prim_type, nsegments);
|
||||
for (int i = 0; i < nsegments; i++) {
|
||||
const float angle = float(2 * M_PI) * (float(i) / float(nsegments));
|
||||
immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
|
||||
{
|
||||
imm_draw_circle(GPU_PRIM_LINE_LOOP, shdr_pos, x, y, radius, radius, nsegments);
|
||||
}
|
||||
|
||||
void imm_draw_circle_fill_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
|
||||
{
|
||||
imm_draw_circle(GPU_PRIM_TRI_FAN, shdr_pos, x, y, radius, radius, nsegments);
|
||||
}
|
||||
|
||||
void imm_draw_circle_wire_aspect_2d(
|
||||
uint shdr_pos, float x, float y, float radius_x, float radius_y, int nsegments)
|
||||
{
|
||||
imm_draw_circle(GPU_PRIM_LINE_LOOP, shdr_pos, x, y, radius_x, radius_y, nsegments);
|
||||
}
|
||||
|
||||
void imm_draw_circle_fill_aspect_2d(
|
||||
uint shdr_pos, float x, float y, float radius_x, float radius_y, int nsegments)
|
||||
{
|
||||
imm_draw_circle(GPU_PRIM_TRI_FAN, shdr_pos, x, y, radius_x, radius_y, nsegments);
|
||||
}
|
||||
|
||||
static void imm_draw_circle_partial(GPUPrimType prim_type,
|
||||
uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float radius,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep)
|
||||
{
|
||||
/* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
|
||||
const float angle_start = -DEG2RADF(start) + float(M_PI_2);
|
||||
const float angle_end = -(DEG2RADF(sweep) - angle_start);
|
||||
nsegments += 1;
|
||||
immBegin(prim_type, nsegments);
|
||||
for (int i = 0; i < nsegments; i++) {
|
||||
const float angle = interpf(angle_start, angle_end, (float(i) / float(nsegments - 1)));
|
||||
const float angle_sin = sinf(angle);
|
||||
const float angle_cos = cosf(angle);
|
||||
immVertex2f(pos, x + radius * angle_cos, y + radius * angle_sin);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
static void imm_draw_circle_partial_3d(GPUPrimType prim_type,
|
||||
uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float rad,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep)
|
||||
{
|
||||
/* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
|
||||
const float angle_start = -DEG2RADF(start) + float(M_PI / 2);
|
||||
const float angle_end = -(DEG2RADF(sweep) - angle_start);
|
||||
nsegments += 1;
|
||||
immBegin(prim_type, nsegments);
|
||||
for (int i = 0; i < nsegments; i++) {
|
||||
const float angle = interpf(angle_start, angle_end, (float(i) / float(nsegments - 1)));
|
||||
const float angle_sin = sinf(angle);
|
||||
const float angle_cos = cosf(angle);
|
||||
immVertex3f(pos, x + rad * angle_cos, y + rad * angle_sin, z);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_circle_partial_wire_2d(
|
||||
uint pos, float x, float y, float radius, int nsegments, float start, float sweep)
|
||||
{
|
||||
imm_draw_circle_partial(GPU_PRIM_LINE_STRIP, pos, x, y, radius, nsegments, start, sweep);
|
||||
}
|
||||
|
||||
void imm_draw_circle_partial_wire_3d(
|
||||
uint pos, float x, float y, float z, float radius, int nsegments, float start, float sweep)
|
||||
{
|
||||
imm_draw_circle_partial_3d(GPU_PRIM_LINE_STRIP, pos, x, y, z, radius, nsegments, start, sweep);
|
||||
}
|
||||
|
||||
static void imm_draw_disk_partial(GPUPrimType prim_type,
|
||||
uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float rad_inner,
|
||||
float rad_outer,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep)
|
||||
{
|
||||
/* to avoid artifacts */
|
||||
const float max_angle = 3 * 360;
|
||||
CLAMP(sweep, -max_angle, max_angle);
|
||||
|
||||
/* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
|
||||
const float angle_start = -DEG2RADF(start) + float(M_PI_2);
|
||||
const float angle_end = -(DEG2RADF(sweep) - angle_start);
|
||||
nsegments += 1;
|
||||
immBegin(prim_type, nsegments * 2);
|
||||
for (int i = 0; i < nsegments; i++) {
|
||||
const float angle = interpf(angle_start, angle_end, (float(i) / float(nsegments - 1)));
|
||||
const float angle_sin = sinf(angle);
|
||||
const float angle_cos = cosf(angle);
|
||||
immVertex2f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin);
|
||||
immVertex2f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
static void imm_draw_disk_partial_3d(GPUPrimType prim_type,
|
||||
uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float rad_inner,
|
||||
float rad_outer,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep)
|
||||
{
|
||||
/* to avoid artifacts */
|
||||
const float max_angle = 3 * 360;
|
||||
CLAMP(sweep, -max_angle, max_angle);
|
||||
|
||||
/* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
|
||||
const float angle_start = -DEG2RADF(start) + float(M_PI_2);
|
||||
const float angle_end = -(DEG2RADF(sweep) - angle_start);
|
||||
nsegments += 1;
|
||||
immBegin(prim_type, nsegments * 2);
|
||||
for (int i = 0; i < nsegments; i++) {
|
||||
const float angle = interpf(angle_start, angle_end, (float(i) / float(nsegments - 1)));
|
||||
const float angle_sin = sinf(angle);
|
||||
const float angle_cos = cosf(angle);
|
||||
immVertex3f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin, z);
|
||||
immVertex3f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin, z);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_disk_partial_fill_2d(uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float rad_inner,
|
||||
float rad_outer,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep)
|
||||
{
|
||||
imm_draw_disk_partial(
|
||||
GPU_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep);
|
||||
}
|
||||
void imm_draw_disk_partial_fill_3d(uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float rad_inner,
|
||||
float rad_outer,
|
||||
int nsegments,
|
||||
float start,
|
||||
float sweep)
|
||||
{
|
||||
imm_draw_disk_partial_3d(
|
||||
GPU_PRIM_TRI_STRIP, pos, x, y, z, rad_inner, rad_outer, nsegments, start, sweep);
|
||||
}
|
||||
|
||||
static void imm_draw_circle_3D(GPUPrimType prim_type,
|
||||
uint pos,
|
||||
float x,
|
||||
float y,
|
||||
float radius_x,
|
||||
float radius_y,
|
||||
int nsegments)
|
||||
{
|
||||
if (prim_type == GPU_PRIM_LINE_LOOP) {
|
||||
/* NOTE(Metal/AMD): For small primitives, line list more efficient than line strip. */
|
||||
immBegin(GPU_PRIM_LINES, nsegments * 2);
|
||||
|
||||
const float angle = float(2 * M_PI) / float(nsegments);
|
||||
float xprev = cosf(-angle) * radius_x;
|
||||
float yprev = sinf(-angle) * radius_y;
|
||||
const float alpha = 2.0f * cosf(angle);
|
||||
|
||||
float xr = radius_x;
|
||||
float yr = 0;
|
||||
|
||||
for (int i = 0; i < nsegments; i++) {
|
||||
immVertex3f(pos, x + xr, y + yr, 0.0f);
|
||||
if (i) {
|
||||
immVertex3f(pos, x + xr, y + yr, 0.0f);
|
||||
}
|
||||
/* `cos[(n + 1)a] = 2cos(a)cos(na) - cos[(n - 1)a]`. */
|
||||
const float xnext = alpha * xr - xprev;
|
||||
/* `sin[(n + 1)a] = 2cos(a)sin(na) - sin[(n - 1)a]`. */
|
||||
const float ynext = alpha * yr - yprev;
|
||||
xprev = xr;
|
||||
yprev = yr;
|
||||
xr = xnext;
|
||||
yr = ynext;
|
||||
}
|
||||
immVertex3f(pos, x + radius_x, y, 0.0f);
|
||||
immEnd();
|
||||
}
|
||||
else {
|
||||
immBegin(prim_type, nsegments);
|
||||
for (int i = 0; i < nsegments; i++) {
|
||||
float angle = float(2 * M_PI) * (float(i) / float(nsegments));
|
||||
immVertex3f(pos, x + radius_x * cosf(angle), y + radius_y * sinf(angle), 0.0f);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments)
|
||||
{
|
||||
imm_draw_circle_3D(GPU_PRIM_LINE_LOOP, pos, x, y, radius, radius, nsegments);
|
||||
}
|
||||
|
||||
void imm_draw_circle_wire_aspect_3d(
|
||||
uint pos, float x, float y, float radius_x, float radius_y, int nsegments)
|
||||
{
|
||||
imm_draw_circle_3D(GPU_PRIM_LINE_LOOP, pos, x, y, radius_x, radius_y, nsegments);
|
||||
}
|
||||
|
||||
void imm_draw_circle_dashed_3d(uint pos, float x, float y, float radius, int nsegments)
|
||||
{
|
||||
imm_draw_circle_3D(GPU_PRIM_LINES, pos, x, y, radius, radius, nsegments / 2);
|
||||
}
|
||||
|
||||
void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegments)
|
||||
{
|
||||
imm_draw_circle_3D(GPU_PRIM_TRI_FAN, pos, x, y, radius, radius, nsegments);
|
||||
}
|
||||
|
||||
void imm_draw_circle_fill_aspect_3d(
|
||||
uint pos, float x, float y, float radius_x, float radius_y, int nsegments)
|
||||
{
|
||||
imm_draw_circle_3D(GPU_PRIM_TRI_FAN, pos, x, y, radius_x, radius_y, nsegments);
|
||||
}
|
||||
|
||||
void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
/* NOTE(Metal/AMD): For small primitives, line list more efficient than line-strip. */
|
||||
immBegin(GPU_PRIM_LINES, 8);
|
||||
immVertex2f(pos, x1, y1);
|
||||
immVertex2f(pos, x1, y2);
|
||||
|
||||
immVertex2f(pos, x1, y2);
|
||||
immVertex2f(pos, x2, y2);
|
||||
|
||||
immVertex2f(pos, x2, y2);
|
||||
immVertex2f(pos, x2, y1);
|
||||
|
||||
immVertex2f(pos, x2, y1);
|
||||
immVertex2f(pos, x1, y1);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
/* use this version when GPUVertFormat has a vec3 position */
|
||||
/* NOTE(Metal/AMD): For small primitives, line list more efficient than line-strip. */
|
||||
immBegin(GPU_PRIM_LINES, 8);
|
||||
immVertex3f(pos, x1, y1, 0.0f);
|
||||
immVertex3f(pos, x1, y2, 0.0f);
|
||||
|
||||
immVertex3f(pos, x1, y2, 0.0f);
|
||||
immVertex3f(pos, x2, y2, 0.0f);
|
||||
|
||||
immVertex3f(pos, x2, y2, 0.0f);
|
||||
immVertex3f(pos, x2, y1, 0.0f);
|
||||
|
||||
immVertex3f(pos, x2, y1, 0.0f);
|
||||
immVertex3f(pos, x1, y1, 0.0f);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_box_checker_2d_ex(float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
const float color_primary[4],
|
||||
const float color_secondary[4],
|
||||
int checker_size)
|
||||
{
|
||||
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_2D_CHECKER);
|
||||
|
||||
immUniform4fv("color1", color_primary);
|
||||
immUniform4fv("color2", color_secondary);
|
||||
immUniform1i("size", checker_size);
|
||||
|
||||
immRectf(pos, x1, y1, x2, y2);
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2, bool clear_alpha)
|
||||
{
|
||||
float checker_primary[4];
|
||||
float checker_secondary[4];
|
||||
ui::theme::get_color_4fv(TH_TRANSPARENT_CHECKER_PRIMARY, checker_primary);
|
||||
ui::theme::get_color_4fv(TH_TRANSPARENT_CHECKER_SECONDARY, checker_secondary);
|
||||
checker_primary[3] = clear_alpha ? 0.0 : checker_primary[3];
|
||||
checker_secondary[3] = clear_alpha ? 0.0 : checker_secondary[3];
|
||||
int checker_size = ui::theme::get_value(TH_TRANSPARENT_CHECKER_SIZE) * U.pixelsize;
|
||||
imm_draw_box_checker_2d_ex(x1, y1, x2, y2, checker_primary, checker_secondary, checker_size);
|
||||
}
|
||||
|
||||
void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3])
|
||||
{
|
||||
float coords[ARRAY_SIZE(cube_coords)][3];
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
|
||||
madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
|
||||
}
|
||||
|
||||
immBegin(GPU_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2);
|
||||
for (int i = 0; i < ARRAY_SIZE(cube_quad_index); i++) {
|
||||
immVertex3fv(pos, coords[cube_quad_index[i][0]]);
|
||||
immVertex3fv(pos, coords[cube_quad_index[i][1]]);
|
||||
immVertex3fv(pos, coords[cube_quad_index[i][2]]);
|
||||
|
||||
immVertex3fv(pos, coords[cube_quad_index[i][0]]);
|
||||
immVertex3fv(pos, coords[cube_quad_index[i][2]]);
|
||||
immVertex3fv(pos, coords[cube_quad_index[i][3]]);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3])
|
||||
{
|
||||
float coords[ARRAY_SIZE(cube_coords)][3];
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
|
||||
madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
|
||||
}
|
||||
|
||||
immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2);
|
||||
for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) {
|
||||
immVertex3fv(pos, coords[cube_line_index[i][0]]);
|
||||
immVertex3fv(pos, coords[cube_line_index[i][1]]);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_cube_corners_3d(uint pos,
|
||||
const float center[3],
|
||||
const float aspect[3],
|
||||
const float factor)
|
||||
{
|
||||
float coords[ARRAY_SIZE(cube_coords)][3];
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
|
||||
madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect);
|
||||
}
|
||||
|
||||
immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4);
|
||||
for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) {
|
||||
float vec[3], co[3];
|
||||
sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]);
|
||||
mul_v3_fl(vec, factor);
|
||||
|
||||
copy_v3_v3(co, coords[cube_line_index[i][0]]);
|
||||
immVertex3fv(pos, co);
|
||||
add_v3_v3(co, vec);
|
||||
immVertex3fv(pos, co);
|
||||
|
||||
copy_v3_v3(co, coords[cube_line_index[i][1]]);
|
||||
immVertex3fv(pos, co);
|
||||
sub_v3_v3(co, vec);
|
||||
immVertex3fv(pos, co);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_cylinder_fill_normal_3d(
|
||||
uint pos, uint nor, float base, float top, float height, int slices, int stacks)
|
||||
{
|
||||
immBegin(GPU_PRIM_TRIS, 6 * slices * stacks);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
const float angle1 = float(2 * M_PI) * (float(i) / float(slices));
|
||||
const float angle2 = float(2 * M_PI) * (float(i + 1) / float(slices));
|
||||
const float cos1 = cosf(angle1);
|
||||
const float sin1 = sinf(angle1);
|
||||
const float cos2 = cosf(angle2);
|
||||
const float sin2 = sinf(angle2);
|
||||
|
||||
for (int j = 0; j < stacks; j++) {
|
||||
float fac1 = float(j) / float(stacks);
|
||||
float fac2 = float(j + 1) / float(stacks);
|
||||
float r1 = base * (1.0f - fac1) + top * fac1;
|
||||
float r2 = base * (1.0f - fac2) + top * fac2;
|
||||
float h1 = height * (float(j) / float(stacks));
|
||||
float h2 = height * (float(j + 1) / float(stacks));
|
||||
|
||||
const float v1[3] = {r1 * cos2, r1 * sin2, h1};
|
||||
const float v2[3] = {r2 * cos2, r2 * sin2, h2};
|
||||
const float v3[3] = {r2 * cos1, r2 * sin1, h2};
|
||||
const float v4[3] = {r1 * cos1, r1 * sin1, h1};
|
||||
float n1[3], n2[3];
|
||||
|
||||
/* calc normals */
|
||||
sub_v3_v3v3(n1, v2, v1);
|
||||
normalize_v3(n1);
|
||||
n1[0] = cos1;
|
||||
n1[1] = sin1;
|
||||
n1[2] = 1 - n1[2];
|
||||
|
||||
sub_v3_v3v3(n2, v3, v4);
|
||||
normalize_v3(n2);
|
||||
n2[0] = cos2;
|
||||
n2[1] = sin2;
|
||||
n2[2] = 1 - n2[2];
|
||||
|
||||
/* first tri */
|
||||
immAttr3fv(nor, n2);
|
||||
immVertex3fv(pos, v1);
|
||||
immVertex3fv(pos, v2);
|
||||
immAttr3fv(nor, n1);
|
||||
immVertex3fv(pos, v3);
|
||||
|
||||
/* second tri */
|
||||
immVertex3fv(pos, v3);
|
||||
immVertex3fv(pos, v4);
|
||||
immAttr3fv(nor, n2);
|
||||
immVertex3fv(pos, v1);
|
||||
}
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_cylinder_wire_3d(
|
||||
uint pos, float base, float top, float height, int slices, int stacks)
|
||||
{
|
||||
immBegin(GPU_PRIM_LINES, 6 * slices * stacks);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
const float angle1 = float(2 * M_PI) * (float(i) / float(slices));
|
||||
const float angle2 = float(2 * M_PI) * (float(i + 1) / float(slices));
|
||||
const float cos1 = cosf(angle1);
|
||||
const float sin1 = sinf(angle1);
|
||||
const float cos2 = cosf(angle2);
|
||||
const float sin2 = sinf(angle2);
|
||||
|
||||
for (int j = 0; j < stacks; j++) {
|
||||
float fac1 = float(j) / float(stacks);
|
||||
float fac2 = float(j + 1) / float(stacks);
|
||||
float r1 = base * (1.0f - fac1) + top * fac1;
|
||||
float r2 = base * (1.0f - fac2) + top * fac2;
|
||||
float h1 = height * (float(j) / float(stacks));
|
||||
float h2 = height * (float(j + 1) / float(stacks));
|
||||
|
||||
const float v1[3] = {r1 * cos2, r1 * sin2, h1};
|
||||
const float v2[3] = {r2 * cos2, r2 * sin2, h2};
|
||||
const float v3[3] = {r2 * cos1, r2 * sin1, h2};
|
||||
const float v4[3] = {r1 * cos1, r1 * sin1, h1};
|
||||
|
||||
immVertex3fv(pos, v1);
|
||||
immVertex3fv(pos, v2);
|
||||
|
||||
immVertex3fv(pos, v2);
|
||||
immVertex3fv(pos, v3);
|
||||
|
||||
immVertex3fv(pos, v1);
|
||||
immVertex3fv(pos, v4);
|
||||
}
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
void imm_draw_cylinder_fill_3d(
|
||||
uint pos, float base, float top, float height, int slices, int stacks)
|
||||
{
|
||||
immBegin(GPU_PRIM_TRIS, 6 * slices * stacks);
|
||||
for (int i = 0; i < slices; i++) {
|
||||
const float angle1 = float(2 * M_PI) * (float(i) / float(slices));
|
||||
const float angle2 = float(2 * M_PI) * (float(i + 1) / float(slices));
|
||||
const float cos1 = cosf(angle1);
|
||||
const float sin1 = sinf(angle1);
|
||||
const float cos2 = cosf(angle2);
|
||||
const float sin2 = sinf(angle2);
|
||||
|
||||
for (int j = 0; j < stacks; j++) {
|
||||
float fac1 = float(j) / float(stacks);
|
||||
float fac2 = float(j + 1) / float(stacks);
|
||||
float r1 = base * (1.0f - fac1) + top * fac1;
|
||||
float r2 = base * (1.0f - fac2) + top * fac2;
|
||||
float h1 = height * (float(j) / float(stacks));
|
||||
float h2 = height * (float(j + 1) / float(stacks));
|
||||
|
||||
const float v1[3] = {r1 * cos2, r1 * sin2, h1};
|
||||
const float v2[3] = {r2 * cos2, r2 * sin2, h2};
|
||||
const float v3[3] = {r2 * cos1, r2 * sin1, h2};
|
||||
const float v4[3] = {r1 * cos1, r1 * sin1, h1};
|
||||
|
||||
/* first tri */
|
||||
immVertex3fv(pos, v1);
|
||||
immVertex3fv(pos, v2);
|
||||
immVertex3fv(pos, v3);
|
||||
|
||||
/* second tri */
|
||||
immVertex3fv(pos, v3);
|
||||
immVertex3fv(pos, v4);
|
||||
immVertex3fv(pos, v1);
|
||||
}
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
/* Circle Drawing - Tables for Optimized Drawing Speed */
|
||||
constexpr static int CIRCLE_RESOL = 32;
|
||||
|
||||
static void circball_array_fill(const float verts[CIRCLE_RESOL][3],
|
||||
const float cent[3],
|
||||
const float radius,
|
||||
const float tmat[4][4])
|
||||
{
|
||||
/* 32 values of sin function (still same result!) */
|
||||
const float sinval[CIRCLE_RESOL] = {
|
||||
0.00000000, 0.20129852, 0.39435585, 0.57126821, 0.72479278, 0.84864425, 0.93775213,
|
||||
0.98846832, 0.99871650, 0.96807711, 0.89780453, 0.79077573, 0.65137248, 0.48530196,
|
||||
0.29936312, 0.10116832, -0.10116832, -0.29936312, -0.48530196, -0.65137248, -0.79077573,
|
||||
-0.89780453, -0.96807711, -0.99871650, -0.98846832, -0.93775213, -0.84864425, -0.72479278,
|
||||
-0.57126821, -0.39435585, -0.20129852, 0.00000000,
|
||||
};
|
||||
|
||||
/* 32 values of cos function (still same result!) */
|
||||
const float cosval[CIRCLE_RESOL] = {
|
||||
1.00000000, 0.97952994, 0.91895781, 0.82076344, 0.68896691, 0.52896401, 0.34730525,
|
||||
0.15142777, -0.05064916, -0.25065253, -0.44039415, -0.61210598, -0.75875812, -0.87434661,
|
||||
-0.95413925, -0.99486932, -0.99486932, -0.95413925, -0.87434661, -0.75875812, -0.61210598,
|
||||
-0.44039415, -0.25065253, -0.05064916, 0.15142777, 0.34730525, 0.52896401, 0.68896691,
|
||||
0.82076344, 0.91895781, 0.97952994, 1.00000000,
|
||||
};
|
||||
|
||||
float vx[3], vy[3];
|
||||
float *viter = (float *)verts;
|
||||
|
||||
mul_v3_v3fl(vx, tmat[0], radius);
|
||||
mul_v3_v3fl(vy, tmat[1], radius);
|
||||
|
||||
for (uint a = 0; a < CIRCLE_RESOL; a++, viter += 3) {
|
||||
viter[0] = cent[0] + sinval[a] * vx[0] + cosval[a] * vy[0];
|
||||
viter[1] = cent[1] + sinval[a] * vx[1] + cosval[a] * vy[1];
|
||||
viter[2] = cent[2] + sinval[a] * vx[2] + cosval[a] * vy[2];
|
||||
}
|
||||
}
|
||||
|
||||
void imm_drawcircball(const float cent[3], float radius, const float tmat[4][4], uint pos)
|
||||
{
|
||||
float verts[CIRCLE_RESOL][3];
|
||||
|
||||
circball_array_fill(verts, cent, radius, tmat);
|
||||
|
||||
immBegin(GPU_PRIM_LINE_LOOP, CIRCLE_RESOL);
|
||||
for (int i = 0; i < CIRCLE_RESOL; i++) {
|
||||
immVertex3fv(pos, verts[i]);
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
547
blender-5.2.0/source/blender/gpu/intern/gpu_index_buffer.cc
Normal file
547
blender-5.2.0/source/blender/gpu/intern/gpu_index_buffer.cc
Normal file
@@ -0,0 +1,547 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU element list (AKA index buffer)
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_compute.hh"
|
||||
#include "GPU_platform.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include <algorithm> /* For `min/max`. */
|
||||
#include <cstring>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name IndexBufBuilder
|
||||
* \{ */
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder,
|
||||
GPUPrimType prim_type,
|
||||
uint index_len,
|
||||
uint vertex_len)
|
||||
{
|
||||
builder->max_allowed_index = vertex_len - 1;
|
||||
builder->max_index_len = index_len;
|
||||
builder->index_len = 0; // start empty
|
||||
builder->index_min = UINT32_MAX;
|
||||
builder->index_max = 0;
|
||||
builder->prim_type = prim_type;
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* Only encode restart indices for restart-compatible primitive types.
|
||||
* Resolves out-of-bounds read error on macOS. Using 0-index will ensure
|
||||
* degenerative primitives when skipping primitives is required and will
|
||||
* incur no additional performance cost for rendering. */
|
||||
if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_METAL)) {
|
||||
/* We will still use restart-indices for point primitives and then
|
||||
* patch these during IndexBuf::init, as we cannot benefit from degenerative
|
||||
* primitives to eliminate these. */
|
||||
builder->restart_index_value = (is_restart_compatible(prim_type) ||
|
||||
prim_type == GPU_PRIM_POINTS) ?
|
||||
RESTART_INDEX :
|
||||
0;
|
||||
}
|
||||
else {
|
||||
builder->restart_index_value = RESTART_INDEX;
|
||||
}
|
||||
#else
|
||||
builder->restart_index_value = RESTART_INDEX;
|
||||
#endif
|
||||
builder->uses_restart_indices = false;
|
||||
builder->data = MEM_new_array_uninitialized<uint>(builder->max_index_len, "IndexBuf data");
|
||||
}
|
||||
|
||||
void GPU_indexbuf_init(GPUIndexBufBuilder *builder,
|
||||
GPUPrimType prim_type,
|
||||
uint prim_len,
|
||||
uint vertex_len)
|
||||
{
|
||||
int verts_per_prim = GPU_indexbuf_primitive_len(prim_type);
|
||||
BLI_assert(verts_per_prim != -1);
|
||||
GPU_indexbuf_init_ex(builder, prim_type, prim_len * uint(verts_per_prim), vertex_len);
|
||||
}
|
||||
|
||||
IndexBuf *GPU_indexbuf_build_on_device(uint index_len)
|
||||
{
|
||||
IndexBuf *elem_ = GPU_indexbuf_calloc();
|
||||
GPU_indexbuf_init_build_on_device(elem_, index_len);
|
||||
return elem_;
|
||||
}
|
||||
|
||||
void GPU_indexbuf_init_build_on_device(IndexBuf *elem, uint index_len)
|
||||
{
|
||||
IndexBuf *elem_ = elem;
|
||||
elem_->init_build_on_device(index_len);
|
||||
}
|
||||
|
||||
MutableSpan<uint32_t> GPU_indexbuf_get_data(GPUIndexBufBuilder *builder)
|
||||
{
|
||||
return {builder->data, builder->max_index_len};
|
||||
}
|
||||
|
||||
void GPU_indexbuf_join(GPUIndexBufBuilder *builder_to, const GPUIndexBufBuilder *builder_from)
|
||||
{
|
||||
BLI_assert(builder_to->data == builder_from->data);
|
||||
builder_to->index_len = max_uu(builder_to->index_len, builder_from->index_len);
|
||||
builder_to->index_min = min_uu(builder_to->index_min, builder_from->index_min);
|
||||
builder_to->index_max = max_uu(builder_to->index_max, builder_from->index_max);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v)
|
||||
{
|
||||
BLI_assert(builder->data != nullptr);
|
||||
BLI_assert(builder->index_len < builder->max_index_len);
|
||||
BLI_assert(v <= builder->max_allowed_index);
|
||||
builder->data[builder->index_len++] = v;
|
||||
builder->index_min = std::min(builder->index_min, v);
|
||||
builder->index_max = std::max(builder->index_max, v);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *builder)
|
||||
{
|
||||
BLI_assert(builder->data != nullptr);
|
||||
BLI_assert(builder->index_len < builder->max_index_len);
|
||||
builder->data[builder->index_len++] = builder->restart_index_value;
|
||||
builder->uses_restart_indices = true;
|
||||
}
|
||||
|
||||
void GPU_indexbuf_add_point_vert(GPUIndexBufBuilder *builder, uint v)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
|
||||
GPU_indexbuf_add_generic_vert(builder, v);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_add_line_verts(GPUIndexBufBuilder *builder, uint v1, uint v2)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_LINES);
|
||||
BLI_assert(v1 != v2);
|
||||
GPU_indexbuf_add_generic_vert(builder, v1);
|
||||
GPU_indexbuf_add_generic_vert(builder, v2);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *builder, uint v1, uint v2, uint v3)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_TRIS);
|
||||
BLI_assert(v1 != v2 && v2 != v3 && v3 != v1);
|
||||
GPU_indexbuf_add_generic_vert(builder, v1);
|
||||
GPU_indexbuf_add_generic_vert(builder, v2);
|
||||
GPU_indexbuf_add_generic_vert(builder, v3);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_add_line_adj_verts(
|
||||
GPUIndexBufBuilder *builder, uint v1, uint v2, uint v3, uint v4)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_LINES_ADJ);
|
||||
BLI_assert(v2 != v3); /* only the line need diff indices */
|
||||
GPU_indexbuf_add_generic_vert(builder, v1);
|
||||
GPU_indexbuf_add_generic_vert(builder, v2);
|
||||
GPU_indexbuf_add_generic_vert(builder, v3);
|
||||
GPU_indexbuf_add_generic_vert(builder, v4);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
|
||||
BLI_assert(elem < builder->max_index_len);
|
||||
builder->data[elem++] = v1;
|
||||
builder->index_min = std::min(builder->index_min, v1);
|
||||
builder->index_max = std::max(builder->index_max, v1);
|
||||
builder->index_len = std::max(builder->index_len, elem);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_LINES);
|
||||
BLI_assert(v1 != v2);
|
||||
BLI_assert(v1 <= builder->max_allowed_index);
|
||||
BLI_assert(v2 <= builder->max_allowed_index);
|
||||
BLI_assert((elem + 1) * 2 <= builder->max_index_len);
|
||||
uint idx = elem * 2;
|
||||
builder->data[idx++] = v1;
|
||||
builder->data[idx++] = v2;
|
||||
builder->index_min = std::min({builder->index_min, v1, v2});
|
||||
builder->index_max = std::max({builder->index_max, v1, v2});
|
||||
builder->index_len = std::max(builder->index_len, idx);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_TRIS);
|
||||
BLI_assert(v1 != v2 && v2 != v3 && v3 != v1);
|
||||
BLI_assert(v1 <= builder->max_allowed_index);
|
||||
BLI_assert(v2 <= builder->max_allowed_index);
|
||||
BLI_assert(v3 <= builder->max_allowed_index);
|
||||
BLI_assert((elem + 1) * 3 <= builder->max_index_len);
|
||||
uint idx = elem * 3;
|
||||
builder->data[idx++] = v1;
|
||||
builder->data[idx++] = v2;
|
||||
builder->data[idx++] = v3;
|
||||
|
||||
builder->index_min = std::min({builder->index_min, v1, v2, v3});
|
||||
builder->index_max = std::max({builder->index_max, v1, v2, v3});
|
||||
builder->index_len = std::max(builder->index_len, idx);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
|
||||
BLI_assert(elem < builder->max_index_len);
|
||||
builder->data[elem++] = builder->restart_index_value;
|
||||
builder->index_len = std::max(builder->index_len, elem);
|
||||
builder->uses_restart_indices = true;
|
||||
}
|
||||
|
||||
void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_LINES);
|
||||
BLI_assert((elem + 1) * 2 <= builder->max_index_len);
|
||||
uint idx = elem * 2;
|
||||
builder->data[idx++] = builder->restart_index_value;
|
||||
builder->data[idx++] = builder->restart_index_value;
|
||||
builder->index_len = std::max(builder->index_len, idx);
|
||||
builder->uses_restart_indices = true;
|
||||
}
|
||||
|
||||
void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
|
||||
{
|
||||
BLI_assert(builder->prim_type == GPU_PRIM_TRIS);
|
||||
BLI_assert((elem + 1) * 3 <= builder->max_index_len);
|
||||
uint idx = elem * 3;
|
||||
builder->data[idx++] = builder->restart_index_value;
|
||||
builder->data[idx++] = builder->restart_index_value;
|
||||
builder->data[idx++] = builder->restart_index_value;
|
||||
builder->index_len = std::max(builder->index_len, idx);
|
||||
builder->uses_restart_indices = true;
|
||||
}
|
||||
|
||||
IndexBuf *GPU_indexbuf_build_curves_on_device(GPUPrimType prim_type,
|
||||
uint curves_num,
|
||||
uint verts_per_curve)
|
||||
{
|
||||
uint64_t dispatch_x_dim = verts_per_curve;
|
||||
if (ELEM(prim_type, GPU_PRIM_LINE_STRIP, GPU_PRIM_TRI_STRIP)) {
|
||||
dispatch_x_dim += 1;
|
||||
}
|
||||
uint64_t grid_x, grid_y, grid_z;
|
||||
uint64_t max_grid_x = GPU_max_work_group_count(0), max_grid_y = GPU_max_work_group_count(1),
|
||||
max_grid_z = GPU_max_work_group_count(2);
|
||||
grid_x = min_uu(max_grid_x, (dispatch_x_dim + 15) / 16);
|
||||
grid_y = (curves_num + 15) / 16;
|
||||
if (grid_y <= max_grid_y) {
|
||||
grid_z = 1;
|
||||
}
|
||||
else {
|
||||
grid_y = grid_z = uint64_t(ceil(sqrt(double(grid_y))));
|
||||
grid_y = min_uu(grid_y, max_grid_y);
|
||||
grid_z = min_uu(grid_z, max_grid_z);
|
||||
}
|
||||
bool tris = (prim_type == GPU_PRIM_TRIS);
|
||||
bool lines = (prim_type == GPU_PRIM_LINES);
|
||||
gpu::Shader *shader = GPU_shader_get_builtin_shader(
|
||||
tris ? GPU_SHADER_INDEXBUF_TRIS :
|
||||
(lines ? GPU_SHADER_INDEXBUF_LINES : GPU_SHADER_INDEXBUF_POINTS));
|
||||
GPU_shader_bind(shader);
|
||||
IndexBuf *ibo = GPU_indexbuf_build_on_device(curves_num * dispatch_x_dim);
|
||||
int resolution;
|
||||
if (tris) {
|
||||
resolution = 6;
|
||||
}
|
||||
else if (lines) {
|
||||
resolution = 2;
|
||||
}
|
||||
else {
|
||||
resolution = 1;
|
||||
}
|
||||
GPU_shader_uniform_1i(shader, "elements_per_curve", dispatch_x_dim / resolution);
|
||||
GPU_shader_uniform_1i(shader, "ncurves", curves_num);
|
||||
GPU_indexbuf_bind_as_ssbo(ibo, GPU_shader_get_ssbo_binding(shader, "out_indices"));
|
||||
GPU_compute_dispatch(shader, grid_x, grid_y, grid_z);
|
||||
|
||||
GPU_memory_barrier(GPU_BARRIER_ELEMENT_ARRAY);
|
||||
GPU_shader_unbind();
|
||||
return ibo;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
namespace gpu {
|
||||
|
||||
IndexBuf::~IndexBuf()
|
||||
{
|
||||
if (!is_subrange_) {
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
}
|
||||
|
||||
void IndexBuf::init(uint indices_len,
|
||||
uint32_t *indices,
|
||||
uint min_index,
|
||||
uint max_index,
|
||||
GPUPrimType prim_type,
|
||||
bool uses_restart_indices)
|
||||
{
|
||||
is_init_ = true;
|
||||
data_ = indices;
|
||||
index_start_ = 0;
|
||||
index_len_ = indices_len;
|
||||
is_empty_ = min_index > max_index;
|
||||
|
||||
/* Patch index buffer to remove restart indices from
|
||||
* non-restart-compatible primitive types. Restart indices
|
||||
* are situationally added to selectively hide vertices.
|
||||
* Metal does not support restart-indices for non-restart-compatible
|
||||
* types, as such we should remove these indices.
|
||||
*
|
||||
* We only need to perform this for point primitives, as
|
||||
* line primitives/triangle primitives can use index 0 for all
|
||||
* vertices to create a degenerative primitive, where all
|
||||
* vertices share the same index and skip rendering via HW
|
||||
* culling. */
|
||||
if (prim_type == GPU_PRIM_POINTS && uses_restart_indices) {
|
||||
this->strip_restart_indices();
|
||||
}
|
||||
|
||||
#if GPU_TRACK_INDEX_RANGE
|
||||
/* Everything remains 32 bit while building to keep things simple.
|
||||
* Find min/max after, then convert to smallest index type possible. */
|
||||
uint range = min_index < max_index ? max_index - min_index : 0;
|
||||
/* count the primitive restart index. */
|
||||
range += 1;
|
||||
|
||||
if (range <= 0xFFFF) {
|
||||
index_type_ = GPU_INDEX_U16;
|
||||
bool do_clamp_indices = false;
|
||||
# ifdef __APPLE__
|
||||
/* NOTE: For the Metal Backend, we use degenerative primitives to hide vertices
|
||||
* which are not restart compatible. When this is done, we need to ensure
|
||||
* that compressed index ranges clamp all index values within the valid
|
||||
* range, rather than maximally clamping against the USHORT restart index
|
||||
* value of 0xFFFFu, as this will cause an out-of-bounds read during
|
||||
* vertex assembly. */
|
||||
do_clamp_indices = GPU_type_matches_ex(
|
||||
GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_METAL);
|
||||
# endif
|
||||
this->squeeze_indices_short(min_index, max_index, prim_type, do_clamp_indices);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void IndexBuf::init_build_on_device(uint index_len)
|
||||
{
|
||||
is_init_ = true;
|
||||
index_start_ = 0;
|
||||
index_len_ = index_len;
|
||||
index_type_ = GPU_INDEX_U32;
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
void IndexBuf::init_subrange(IndexBuf *elem_src, uint start, uint length)
|
||||
{
|
||||
/* We don't support nested sub-ranges. */
|
||||
BLI_assert(elem_src && elem_src->is_subrange_ == false);
|
||||
BLI_assert((length == 0) || (start + length <= elem_src->index_len_));
|
||||
|
||||
is_init_ = true;
|
||||
is_subrange_ = true;
|
||||
src_ = elem_src;
|
||||
index_start_ = start;
|
||||
index_len_ = length;
|
||||
index_base_ = elem_src->index_base_;
|
||||
index_type_ = elem_src->index_type_;
|
||||
}
|
||||
|
||||
void IndexBuf::squeeze_indices_short(uint min_idx,
|
||||
uint max_idx,
|
||||
GPUPrimType prim_type,
|
||||
bool clamp_indices_in_range)
|
||||
{
|
||||
/* data will never be *larger* than builder->data...
|
||||
* converting in place to avoid extra allocation */
|
||||
uint16_t *ushort_idx = static_cast<uint16_t *>(data_);
|
||||
const uint32_t *uint_idx = static_cast<uint32_t *>(data_);
|
||||
|
||||
if (max_idx >= 0xFFFF) {
|
||||
index_base_ = min_idx;
|
||||
/* NOTE: When using restart_index=0 for degenerative primitives indices,
|
||||
* the compressed index will go below zero and wrap around when min_idx > 0.
|
||||
* In order to ensure the resulting index is still within range, we instead
|
||||
* clamp index to the maximum within the index range.
|
||||
*
|
||||
* `clamp_max_idx` represents the maximum possible index to clamp against. If primitive is
|
||||
* restart-compatible, we can just clamp against the primitive-restart value, otherwise, we
|
||||
* must assign to a valid index within the range.
|
||||
*
|
||||
* NOTE: For OpenGL we skip this by disabling clamping, as we still need to use
|
||||
* restart index values for point primitives to disable rendering. */
|
||||
uint16_t clamp_max_idx = (is_restart_compatible(prim_type) || !clamp_indices_in_range) ?
|
||||
0xFFFFu :
|
||||
(max_idx - min_idx);
|
||||
for (uint i = 0; i < index_len_; i++) {
|
||||
ushort_idx[i] = std::min<uint16_t>(clamp_max_idx, uint_idx[i] - min_idx);
|
||||
}
|
||||
}
|
||||
else {
|
||||
index_base_ = 0;
|
||||
for (uint i = 0; i < index_len_; i++) {
|
||||
ushort_idx[i] = uint16_t(uint_idx[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name C-API
|
||||
* \{ */
|
||||
|
||||
IndexBuf *GPU_indexbuf_calloc()
|
||||
{
|
||||
return GPUBackend::get()->indexbuf_alloc();
|
||||
}
|
||||
|
||||
IndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder)
|
||||
{
|
||||
IndexBuf *elem = GPU_indexbuf_calloc();
|
||||
GPU_indexbuf_build_in_place(builder, elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
IndexBuf *GPU_indexbuf_create_subrange(IndexBuf *elem_src, uint start, uint length)
|
||||
{
|
||||
IndexBuf *elem = GPU_indexbuf_calloc();
|
||||
GPU_indexbuf_create_subrange_in_place(elem, elem_src, start, length);
|
||||
return elem;
|
||||
}
|
||||
|
||||
void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *builder, IndexBuf *elem)
|
||||
{
|
||||
BLI_assert(builder->data != nullptr);
|
||||
/* Transfer data ownership to IndexBuf.
|
||||
* It will be uploaded upon first use. */
|
||||
elem->init(builder->index_len,
|
||||
builder->data,
|
||||
builder->index_min,
|
||||
builder->index_max,
|
||||
builder->prim_type,
|
||||
builder->uses_restart_indices);
|
||||
builder->data = nullptr;
|
||||
}
|
||||
|
||||
void GPU_indexbuf_build_in_place_ex(GPUIndexBufBuilder *builder,
|
||||
const uint index_min,
|
||||
const uint index_max,
|
||||
const bool uses_restart_indices,
|
||||
IndexBuf *elem)
|
||||
{
|
||||
BLI_assert(builder->data != nullptr);
|
||||
/* Transfer data ownership to IndexBuf.
|
||||
* It will be uploaded upon first use. */
|
||||
elem->init(builder->max_index_len,
|
||||
builder->data,
|
||||
index_min,
|
||||
index_max,
|
||||
builder->prim_type,
|
||||
uses_restart_indices);
|
||||
builder->data = nullptr;
|
||||
}
|
||||
|
||||
IndexBuf *GPU_indexbuf_build_ex(GPUIndexBufBuilder *builder,
|
||||
const uint index_min,
|
||||
const uint index_max,
|
||||
const bool uses_restart_indices)
|
||||
{
|
||||
IndexBuf *elem = GPU_indexbuf_calloc();
|
||||
GPU_indexbuf_build_in_place_ex(builder, index_min, index_max, uses_restart_indices, elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
IndexBuf *GPU_indexbuf_build_from_memory(const GPUPrimType prim_type,
|
||||
const uint32_t *data,
|
||||
const int32_t data_len,
|
||||
const int32_t index_min,
|
||||
const int32_t index_max,
|
||||
const bool uses_restart_indices)
|
||||
{
|
||||
const uint32_t indices_num = data_len * indices_per_primitive(prim_type);
|
||||
/* TODO: The need for this copy is meant to be temporary. The data should be uploaded directly to
|
||||
* the GPU here rather than copied to an array owned by the IBO first. */
|
||||
uint32_t *copy = MEM_new_array_uninitialized<uint32_t>(indices_num, __func__);
|
||||
threading::memory_bandwidth_bound_task(sizeof(uint32_t) * indices_num * 2, [&]() {
|
||||
array_utils::copy(Span(data, indices_num), MutableSpan(copy, indices_num));
|
||||
});
|
||||
IndexBuf *ibo = GPU_indexbuf_calloc();
|
||||
ibo->init(indices_num, copy, index_min, index_max, prim_type, uses_restart_indices);
|
||||
return ibo;
|
||||
}
|
||||
|
||||
void GPU_indexbuf_create_subrange_in_place(IndexBuf *elem,
|
||||
IndexBuf *elem_src,
|
||||
uint start,
|
||||
uint length)
|
||||
{
|
||||
elem->init_subrange(elem_src, start, length);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_read(IndexBuf *elem, uint32_t *data)
|
||||
{
|
||||
elem->read(data);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_discard(IndexBuf *elem)
|
||||
{
|
||||
delete elem;
|
||||
}
|
||||
|
||||
bool GPU_indexbuf_is_init(IndexBuf *elem)
|
||||
{
|
||||
return elem->is_init();
|
||||
}
|
||||
|
||||
int GPU_indexbuf_primitive_len(GPUPrimType prim_type)
|
||||
{
|
||||
return indices_per_primitive(prim_type);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_use(IndexBuf *elem)
|
||||
{
|
||||
elem->upload_data();
|
||||
}
|
||||
|
||||
void GPU_indexbuf_bind_as_ssbo(IndexBuf *elem, int binding)
|
||||
{
|
||||
elem->bind_as_ssbo(binding);
|
||||
}
|
||||
|
||||
void GPU_indexbuf_update_sub(IndexBuf *elem, uint start, uint len, const void *data)
|
||||
{
|
||||
elem->update_sub(start, len, data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
69
blender-5.2.0/source/blender/gpu/intern/gpu_init_exit.cc
Normal file
69
blender-5.2.0/source/blender/gpu/intern/gpu_init_exit.cc
Normal file
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BKE_material.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_init_exit.hh" /* interface */
|
||||
#include "GPU_pass.hh"
|
||||
|
||||
#include "intern/gpu_private.hh"
|
||||
#include "intern/gpu_shader_create_info_private.hh"
|
||||
#include "intern/gpu_shader_dependency_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* although the order of initialization and shutdown should not matter
|
||||
* (except for the extensions), I chose alphabetical and reverse alphabetical order
|
||||
*/
|
||||
static bool initialized = false;
|
||||
|
||||
void GPU_init()
|
||||
{
|
||||
/* can't avoid calling this multiple times, see wm_window_ghostwindow_add */
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
gpu_backend_init_resources();
|
||||
|
||||
gpu_shader_dependency_init();
|
||||
gpu_shader_create_info_init();
|
||||
|
||||
GPU_shader_builtin_warm_up();
|
||||
GPU_pass_cache_init();
|
||||
|
||||
gpu_batch_init();
|
||||
}
|
||||
|
||||
void GPU_exit()
|
||||
{
|
||||
gpu_batch_exit();
|
||||
|
||||
GPU_pass_cache_free();
|
||||
|
||||
BKE_material_defaults_free_gpu();
|
||||
GPU_shader_free_builtin_shaders();
|
||||
|
||||
gpu_backend_delete_resources();
|
||||
|
||||
gpu_shader_dependency_exit();
|
||||
gpu_shader_create_info_exit();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
bool GPU_is_init()
|
||||
{
|
||||
return initialized;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
600
blender-5.2.0/source/blender/gpu/intern/gpu_material.cc
Normal file
600
blender-5.2.0/source/blender/gpu/intern/gpu_material.cc
Normal file
@@ -0,0 +1,600 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Manages materials, lights and textures.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_time.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "NOD_shader.h"
|
||||
#include "NOD_shader_nodes_inline.hh"
|
||||
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_pass.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_uniform_buffer.hh"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
|
||||
#include "gpu_node_graph.hh"
|
||||
|
||||
#include "atomic_ops.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void gpu_material_ramp_texture_build(GPUMaterial *mat);
|
||||
static void gpu_material_sky_texture_build(GPUMaterial *mat);
|
||||
|
||||
/* Structs */
|
||||
#define MAX_COLOR_BAND 128
|
||||
#define MAX_GPU_SKIES 8
|
||||
|
||||
struct GPUColorBandBuilder {
|
||||
float pixels[MAX_COLOR_BAND][CM_TABLE + 1][4];
|
||||
int current_layer;
|
||||
};
|
||||
|
||||
struct GPUSkyBuilder {
|
||||
float pixels[MAX_GPU_SKIES][GPU_SKY_WIDTH * GPU_SKY_HEIGHT][4];
|
||||
int current_layer;
|
||||
};
|
||||
|
||||
struct GPUMaterial {
|
||||
/* Contains #gpu::Shader and source code for deferred compilation.
|
||||
* Can be shared between materials sharing same node-tree topology. */
|
||||
GPUPass *pass = nullptr;
|
||||
/* Optimized GPUPass, situationally compiled after initial pass for optimal realtime performance.
|
||||
* This shader variant bakes dynamic uniform data as constant. This variant will not use
|
||||
* the ubo, and instead bake constants directly into the shader source. */
|
||||
GPUPass *optimized_pass = nullptr;
|
||||
|
||||
/* UBOs for this material parameters. */
|
||||
gpu::UniformBuf *ubo = nullptr;
|
||||
/* Some flags about the nodetree & the needed resources. */
|
||||
eGPUMaterialFlag flag = GPU_MATFLAG_UPDATED;
|
||||
/* The engine type this material is compiled for. */
|
||||
eGPUMaterialEngine engine;
|
||||
/* Identify shader variations (shadow, probe, world background...) */
|
||||
uint64_t uuid = 0;
|
||||
/* Number of generated function. */
|
||||
int generated_function_len = 0;
|
||||
|
||||
/* Source material, might be null. */
|
||||
Material *source_material = nullptr;
|
||||
/* 1D Texture array containing all color bands. */
|
||||
gpu::Texture *coba_tex = nullptr;
|
||||
/* Builder for coba_tex. */
|
||||
GPUColorBandBuilder *coba_builder = nullptr;
|
||||
/* 2D Texture array containing all sky textures. */
|
||||
gpu::Texture *sky_tex = nullptr;
|
||||
/* Builder for sky_tex. */
|
||||
GPUSkyBuilder *sky_builder = nullptr;
|
||||
/* Low level node graph(s). Also contains resources needed by the material. */
|
||||
GPUNodeGraph graph = {};
|
||||
|
||||
bool has_surface_output = false;
|
||||
bool has_volume_output = false;
|
||||
bool has_displacement_output = false;
|
||||
|
||||
std::string name;
|
||||
|
||||
GPUMaterial(eGPUMaterialEngine engine) : engine(engine) {};
|
||||
|
||||
~GPUMaterial()
|
||||
{
|
||||
gpu_node_graph_free(&graph);
|
||||
|
||||
if (optimized_pass != nullptr) {
|
||||
GPU_pass_release(optimized_pass);
|
||||
}
|
||||
if (pass != nullptr) {
|
||||
GPU_pass_release(pass);
|
||||
}
|
||||
if (ubo != nullptr) {
|
||||
GPU_uniformbuf_free(ubo);
|
||||
}
|
||||
if (coba_builder != nullptr) {
|
||||
MEM_delete(coba_builder);
|
||||
}
|
||||
if (coba_tex != nullptr) {
|
||||
GPU_texture_free(coba_tex);
|
||||
}
|
||||
if (sky_tex != nullptr) {
|
||||
GPU_texture_free(sky_tex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Public API */
|
||||
|
||||
GPUMaterialFromNodeTreeResult GPU_material_from_nodetree(
|
||||
Material *ma,
|
||||
bNodeTree *ntree,
|
||||
ListBaseT<LinkData> *gpumaterials,
|
||||
const char *name,
|
||||
eGPUMaterialEngine engine,
|
||||
uint64_t shader_uuid,
|
||||
bool deferred_compilation,
|
||||
GPUCodegenCallbackFn callback,
|
||||
void *thunk,
|
||||
GPUMaterialPassReplacementCallbackFn pass_replacement_cb)
|
||||
{
|
||||
/* Search if this material is not already compiled. */
|
||||
for (LinkData &link : *gpumaterials) {
|
||||
GPUMaterial *mat = static_cast<GPUMaterial *>(link.data);
|
||||
if (mat->uuid == shader_uuid && mat->engine == engine) {
|
||||
if (!deferred_compilation) {
|
||||
GPU_pass_ensure_its_ready(mat->pass);
|
||||
}
|
||||
return {mat};
|
||||
}
|
||||
}
|
||||
|
||||
GPUMaterialFromNodeTreeResult result;
|
||||
|
||||
GPUMaterial *mat = MEM_new<GPUMaterial>(__func__, engine);
|
||||
mat->source_material = ma;
|
||||
mat->uuid = shader_uuid;
|
||||
mat->name = name;
|
||||
result.material = mat;
|
||||
|
||||
/* Localize tree to create links for reroute and mute. */
|
||||
bNodeTree *localtree = bke::node_tree_add_tree(
|
||||
nullptr, (StringRef(ntree->id.name) + " Inlined").c_str(), ntree->idname);
|
||||
localtree->flag |= NTREE_IS_GPU_SHADER_INTERNAL;
|
||||
nodes::InlineShaderNodeTreeParams inline_params;
|
||||
inline_params.allow_preserving_repeat_zones = true;
|
||||
inline_params.target_engine_ = engine == GPU_MAT_EEVEE ? SHD_OUTPUT_EEVEE : SHD_OUTPUT_ALL;
|
||||
nodes::inline_shader_node_tree(*ntree, *localtree, inline_params);
|
||||
|
||||
for (nodes::InlineShaderNodeTreeParams::ErrorMessage &error : inline_params.r_error_messages) {
|
||||
result.errors.append({error.node, std::move(error.message)});
|
||||
}
|
||||
|
||||
ntreeGPUMaterialNodes(localtree, mat);
|
||||
|
||||
gpu_material_ramp_texture_build(mat);
|
||||
gpu_material_sky_texture_build(mat);
|
||||
|
||||
/* Use default material pass when possible. */
|
||||
if (GPUPass *default_pass = pass_replacement_cb ? pass_replacement_cb(thunk, mat) : nullptr) {
|
||||
mat->pass = default_pass;
|
||||
GPU_pass_acquire(mat->pass);
|
||||
/** WORKAROUND:
|
||||
* The node tree code is never executed in default replaced passes,
|
||||
* but the GPU validation will still complain if the node tree UBO is not bound.
|
||||
* So we create a dummy UBO with (at least) the size of the default material one (192 bytes).
|
||||
* We allocate 256 bytes to leave some room for future changes. */
|
||||
mat->ubo = GPU_uniformbuf_create_ex(256, nullptr, "Dummy UBO");
|
||||
}
|
||||
else {
|
||||
/* Create source code and search pass cache for an already compiled version. */
|
||||
mat->pass = GPU_generate_pass(
|
||||
mat, &mat->graph, mat->name.c_str(), engine, deferred_compilation, callback, thunk, false);
|
||||
}
|
||||
|
||||
/* Determine whether we should generate an optimized variant of the graph.
|
||||
* Heuristic is based on complexity of default material pass and shader node graph. */
|
||||
if (GPU_pass_should_optimize(mat->pass)) {
|
||||
mat->optimized_pass = GPU_generate_pass(
|
||||
mat, &mat->graph, mat->name.c_str(), engine, true, callback, thunk, true);
|
||||
}
|
||||
|
||||
gpu_node_graph_free_nodes(&mat->graph);
|
||||
/* Only free after GPU_pass_shader_get where gpu::UniformBuf read data from the local
|
||||
* tree. */
|
||||
BKE_id_free(nullptr, &localtree->id);
|
||||
|
||||
/* Note that even if building the shader fails in some way, we want to keep
|
||||
* it to avoid trying to compile again and again, and simply do not use
|
||||
* the actual shader on drawing. */
|
||||
LinkData *link = MEM_new_zeroed<LinkData>("GPUMaterialLink");
|
||||
link->data = mat;
|
||||
BLI_addtail(gpumaterials, link);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
GPUMaterial *GPU_material_from_callbacks(eGPUMaterialEngine engine,
|
||||
ConstructGPUMaterialFn construct_function_cb,
|
||||
GPUCodegenCallbackFn generate_code_function_cb,
|
||||
void *thunk)
|
||||
{
|
||||
/* Allocate a new material and its material graph. */
|
||||
GPUMaterial *material = MEM_new<GPUMaterial>(__func__, engine);
|
||||
|
||||
/* Construct the material graph by adding and linking the necessary GPU material nodes. */
|
||||
construct_function_cb(thunk, material);
|
||||
|
||||
/* Create and initialize the texture storing color bands used by Ramp and Curve nodes. */
|
||||
gpu_material_ramp_texture_build(material);
|
||||
|
||||
/* Lookup an existing pass in the cache or generate a new one. */
|
||||
material->pass = GPU_generate_pass(material,
|
||||
&material->graph,
|
||||
__func__,
|
||||
engine,
|
||||
false,
|
||||
generate_code_function_cb,
|
||||
thunk,
|
||||
false);
|
||||
|
||||
/* Determine whether we should generate an optimized variant of the graph.
|
||||
* Heuristic is based on complexity of default material pass and shader node graph. */
|
||||
if (GPU_pass_should_optimize(material->pass)) {
|
||||
material->optimized_pass = GPU_generate_pass(material,
|
||||
&material->graph,
|
||||
__func__,
|
||||
engine,
|
||||
true,
|
||||
generate_code_function_cb,
|
||||
thunk,
|
||||
true);
|
||||
}
|
||||
|
||||
gpu_node_graph_free_nodes(&material->graph);
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
void GPU_material_free_single(GPUMaterial *material)
|
||||
{
|
||||
MEM_delete(material);
|
||||
}
|
||||
|
||||
void GPU_material_free(ListBaseT<LinkData> *gpumaterial)
|
||||
{
|
||||
for (LinkData &link : *gpumaterial) {
|
||||
GPUMaterial *material = static_cast<GPUMaterial *>(link.data);
|
||||
GPU_material_free_single(material);
|
||||
}
|
||||
gpumaterial->free_no_destruct();
|
||||
}
|
||||
|
||||
void GPU_materials_free(Main *bmain)
|
||||
{
|
||||
for (Material &ma : bmain->materials) {
|
||||
GPU_material_free(&ma.gpumaterial);
|
||||
}
|
||||
|
||||
for (World &wo : bmain->worlds) {
|
||||
GPU_material_free(&wo.gpumaterial);
|
||||
}
|
||||
|
||||
BKE_material_defaults_free_gpu();
|
||||
}
|
||||
|
||||
const char *GPU_material_get_name(GPUMaterial *material)
|
||||
{
|
||||
return material->name.c_str();
|
||||
}
|
||||
|
||||
uint64_t GPU_material_uuid_get(GPUMaterial *mat)
|
||||
{
|
||||
return mat->uuid;
|
||||
}
|
||||
|
||||
Material *GPU_material_get_material(GPUMaterial *material)
|
||||
{
|
||||
return material->source_material;
|
||||
}
|
||||
|
||||
GPUPass *GPU_material_get_pass(GPUMaterial *material)
|
||||
{
|
||||
/* If an optimized pass variant is available, and optimization is
|
||||
* flagged as complete, we use this one instead. */
|
||||
return GPU_material_optimization_status(material) == GPU_MAT_OPTIMIZATION_SUCCESS ?
|
||||
material->optimized_pass :
|
||||
material->pass;
|
||||
}
|
||||
|
||||
gpu::Shader *GPU_material_get_shader(GPUMaterial *material)
|
||||
{
|
||||
return GPU_pass_shader_get(GPU_material_get_pass(material));
|
||||
}
|
||||
|
||||
GPUMaterialStatus GPU_material_status(GPUMaterial *mat)
|
||||
{
|
||||
switch (GPU_pass_status(mat->pass)) {
|
||||
case GPU_PASS_SUCCESS:
|
||||
return GPU_MAT_SUCCESS;
|
||||
case GPU_PASS_QUEUED:
|
||||
return GPU_MAT_QUEUED;
|
||||
default:
|
||||
return GPU_MAT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
eGPUMaterialOptimizationStatus GPU_material_optimization_status(GPUMaterial *mat)
|
||||
{
|
||||
if (!GPU_pass_should_optimize(mat->pass)) {
|
||||
return GPU_MAT_OPTIMIZATION_SKIP;
|
||||
}
|
||||
|
||||
switch (GPU_pass_status(mat->optimized_pass)) {
|
||||
case GPU_PASS_SUCCESS:
|
||||
return GPU_MAT_OPTIMIZATION_SUCCESS;
|
||||
case GPU_PASS_QUEUED:
|
||||
return GPU_MAT_OPTIMIZATION_QUEUED;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return GPU_MAT_OPTIMIZATION_SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GPU_material_compilation_timestamp(GPUMaterial *mat)
|
||||
{
|
||||
return GPU_pass_compilation_timestamp(mat->pass);
|
||||
}
|
||||
|
||||
bool GPU_material_has_surface_output(GPUMaterial *mat)
|
||||
{
|
||||
return mat->has_surface_output;
|
||||
}
|
||||
|
||||
bool GPU_material_has_volume_output(GPUMaterial *mat)
|
||||
{
|
||||
return mat->has_volume_output;
|
||||
}
|
||||
|
||||
bool GPU_material_has_displacement_output(GPUMaterial *mat)
|
||||
{
|
||||
return mat->has_displacement_output;
|
||||
}
|
||||
|
||||
bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag)
|
||||
{
|
||||
return (mat->flag & flag) != 0;
|
||||
}
|
||||
|
||||
eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat)
|
||||
{
|
||||
return mat->flag;
|
||||
}
|
||||
|
||||
void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag)
|
||||
{
|
||||
if ((flag & GPU_MATFLAG_GLOSSY) && (mat->flag & GPU_MATFLAG_GLOSSY)) {
|
||||
/* Tag material using multiple glossy BSDF as using clear coat. */
|
||||
mat->flag |= GPU_MATFLAG_COAT;
|
||||
}
|
||||
mat->flag |= flag;
|
||||
}
|
||||
|
||||
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBaseT<LinkData> *inputs)
|
||||
{
|
||||
material->ubo = GPU_uniformbuf_create_from_list(inputs, material->name.c_str());
|
||||
}
|
||||
|
||||
gpu::UniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material)
|
||||
{
|
||||
return material->ubo;
|
||||
}
|
||||
|
||||
ListBaseT<GPUMaterialAttribute> GPU_material_attributes(const GPUMaterial *material)
|
||||
{
|
||||
return material->graph.attributes;
|
||||
}
|
||||
|
||||
ListBaseT<GPUMaterialTexture> GPU_material_textures(GPUMaterial *material)
|
||||
{
|
||||
return material->graph.textures;
|
||||
}
|
||||
|
||||
const GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material)
|
||||
{
|
||||
const GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
|
||||
return attrs->count > 0 ? attrs : nullptr;
|
||||
}
|
||||
|
||||
const ListBaseT<GPULayerAttr> *GPU_material_layer_attributes(const GPUMaterial *material)
|
||||
{
|
||||
const ListBaseT<GPULayerAttr> *attrs = &material->graph.layer_attrs;
|
||||
return !attrs->is_empty() ? attrs : nullptr;
|
||||
}
|
||||
|
||||
GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material)
|
||||
{
|
||||
return &material->graph;
|
||||
}
|
||||
|
||||
/* Resources */
|
||||
|
||||
gpu::Texture **gpu_material_sky_texture_layer_set(
|
||||
GPUMaterial *mat, int width, int height, const float *pixels, float *row)
|
||||
{
|
||||
/* In order to put all sky textures into one 2D array texture,
|
||||
* we need them to be the same size. */
|
||||
BLI_assert(width == GPU_SKY_WIDTH);
|
||||
BLI_assert(height == GPU_SKY_HEIGHT);
|
||||
UNUSED_VARS_NDEBUG(width, height);
|
||||
|
||||
if (mat->sky_builder == nullptr) {
|
||||
mat->sky_builder = MEM_new_uninitialized<GPUSkyBuilder>("GPUSkyBuilder");
|
||||
mat->sky_builder->current_layer = 0;
|
||||
}
|
||||
|
||||
int layer = mat->sky_builder->current_layer;
|
||||
*row = float(layer);
|
||||
|
||||
if (*row == MAX_GPU_SKIES) {
|
||||
printf("Too many sky textures in shader!\n");
|
||||
}
|
||||
else {
|
||||
float *dst = reinterpret_cast<float *>(mat->sky_builder->pixels[layer]);
|
||||
memcpy(dst, pixels, sizeof(float) * GPU_SKY_WIDTH * GPU_SKY_HEIGHT * 4);
|
||||
mat->sky_builder->current_layer += 1;
|
||||
}
|
||||
|
||||
return &mat->sky_tex;
|
||||
}
|
||||
|
||||
gpu::Texture **gpu_material_ramp_texture_row_set(GPUMaterial *mat,
|
||||
int size,
|
||||
const float *pixels,
|
||||
float *r_row)
|
||||
{
|
||||
/* In order to put all the color-bands into one 1D array texture,
|
||||
* we need them to be the same size. */
|
||||
BLI_assert(size == CM_TABLE + 1);
|
||||
UNUSED_VARS_NDEBUG(size);
|
||||
|
||||
if (mat->coba_builder == nullptr) {
|
||||
mat->coba_builder = MEM_new_uninitialized<GPUColorBandBuilder>("GPUColorBandBuilder");
|
||||
mat->coba_builder->current_layer = 0;
|
||||
}
|
||||
|
||||
int layer = mat->coba_builder->current_layer;
|
||||
*r_row = float(layer);
|
||||
|
||||
if (*r_row == MAX_COLOR_BAND) {
|
||||
printf("Too many color band in shader! Remove some Curve, Black Body or Color Ramp Node.\n");
|
||||
}
|
||||
else {
|
||||
float *dst = reinterpret_cast<float *>(mat->coba_builder->pixels[layer]);
|
||||
memcpy(dst, pixels, sizeof(float) * (CM_TABLE + 1) * 4);
|
||||
mat->coba_builder->current_layer += 1;
|
||||
}
|
||||
|
||||
return &mat->coba_tex;
|
||||
}
|
||||
|
||||
static void gpu_material_ramp_texture_build(GPUMaterial *mat)
|
||||
{
|
||||
if (mat->coba_builder == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPUColorBandBuilder *builder = mat->coba_builder;
|
||||
|
||||
mat->coba_tex = GPU_texture_create_1d_array("mat_ramp",
|
||||
CM_TABLE + 1,
|
||||
builder->current_layer,
|
||||
1,
|
||||
gpu::TextureFormat::SFLOAT_16_16_16_16,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
reinterpret_cast<float *>(builder->pixels));
|
||||
|
||||
MEM_delete(builder);
|
||||
mat->coba_builder = nullptr;
|
||||
}
|
||||
|
||||
static void gpu_material_sky_texture_build(GPUMaterial *mat)
|
||||
{
|
||||
if (mat->sky_builder == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
mat->sky_tex = GPU_texture_create_2d_array("mat_sky",
|
||||
GPU_SKY_WIDTH,
|
||||
GPU_SKY_HEIGHT,
|
||||
mat->sky_builder->current_layer,
|
||||
1,
|
||||
gpu::TextureFormat::SFLOAT_32_32_32_32,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
reinterpret_cast<float *>(mat->sky_builder->pixels));
|
||||
|
||||
MEM_delete(mat->sky_builder);
|
||||
mat->sky_builder = nullptr;
|
||||
}
|
||||
|
||||
/* Code generation */
|
||||
|
||||
void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link)
|
||||
{
|
||||
if (!material->graph.outlink_surface) {
|
||||
material->graph.outlink_surface = link;
|
||||
material->has_surface_output = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link)
|
||||
{
|
||||
if (!material->graph.outlink_volume) {
|
||||
material->graph.outlink_volume = link;
|
||||
material->has_volume_output = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link)
|
||||
{
|
||||
if (!material->graph.outlink_displacement) {
|
||||
material->graph.outlink_displacement = link;
|
||||
material->has_displacement_output = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link)
|
||||
{
|
||||
if (!material->graph.outlink_thickness) {
|
||||
material->graph.outlink_thickness = link;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash)
|
||||
{
|
||||
GPUNodeGraphOutputLink *aov_link = MEM_new_zeroed<GPUNodeGraphOutputLink>(__func__);
|
||||
aov_link->outlink = link;
|
||||
aov_link->hash = hash;
|
||||
BLI_addtail(&material->graph.outlink_aovs, aov_link);
|
||||
}
|
||||
|
||||
void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link)
|
||||
{
|
||||
GPUNodeGraphOutputLink *compositor_link = MEM_new_zeroed<GPUNodeGraphOutputLink>(__func__);
|
||||
compositor_link->outlink = link;
|
||||
BLI_addtail(&material->graph.outlink_compositor, compositor_link);
|
||||
}
|
||||
|
||||
char *GPU_material_split_sub_function(GPUMaterial *material,
|
||||
GPUType return_type,
|
||||
GPUNodeLink **link)
|
||||
{
|
||||
/* Force cast to return type. */
|
||||
switch (return_type) {
|
||||
case GPU_FLOAT:
|
||||
GPU_link(material, "set_value", *link, link);
|
||||
break;
|
||||
case GPU_VEC3:
|
||||
GPU_link(material, "set_rgb", *link, link);
|
||||
break;
|
||||
case GPU_VEC4:
|
||||
GPU_link(material, "set_rgba", *link, link);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
GPUNodeGraphFunctionLink *func_link = MEM_new_zeroed<GPUNodeGraphFunctionLink>(__func__);
|
||||
func_link->outlink = *link;
|
||||
SNPRINTF(func_link->name, "ntree_fn%d", material->generated_function_len++);
|
||||
BLI_addtail(&material->graph.material_functions, func_link);
|
||||
|
||||
return func_link->name;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Parsing of and code generation using GLSL shaders in gpu/shaders/material. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_set.hh"
|
||||
#include "GPU_material.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define MAX_FUNCTION_NAME 64
|
||||
#define MAX_PARAMETER 36
|
||||
|
||||
enum GPUFunctionQual {
|
||||
FUNCTION_QUAL_IN,
|
||||
FUNCTION_QUAL_OUT,
|
||||
FUNCTION_QUAL_INOUT,
|
||||
};
|
||||
|
||||
struct GPUFunction {
|
||||
char name[MAX_FUNCTION_NAME];
|
||||
GPUType paramtype[MAX_PARAMETER];
|
||||
GPUFunctionQual paramqual[MAX_PARAMETER];
|
||||
int totparam;
|
||||
/* TODO(@fclem): Clean that void pointer. */
|
||||
void *source; /* GPUSource */
|
||||
};
|
||||
|
||||
GPUFunction *gpu_material_library_get_function(const char *name);
|
||||
void gpu_material_library_use_function(Set<StringRefNull> &used_libraries, const char *name);
|
||||
|
||||
} // namespace blender
|
||||
753
blender-5.2.0/source/blender/gpu/intern/gpu_matrix.cc
Normal file
753
blender-5.2.0/source/blender/gpu/intern/gpu_matrix.cc
Normal file
@@ -0,0 +1,753 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_matrix_private.hh"
|
||||
|
||||
#define SUPPRESS_GENERIC_MATRIX_API
|
||||
#define USE_GPU_PY_MATRIX_API /* only so values are declared */
|
||||
#include "GPU_matrix.hh"
|
||||
#undef USE_GPU_PY_MATRIX_API
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
constexpr static int MATRIX_STACK_DEPTH = 32;
|
||||
|
||||
using Mat4 = float[4][4];
|
||||
using Mat3 = float[3][3];
|
||||
|
||||
struct MatrixStack {
|
||||
Mat4 stack[MATRIX_STACK_DEPTH];
|
||||
uint top;
|
||||
};
|
||||
|
||||
struct GPUMatrixState {
|
||||
MatrixStack model_view_stack;
|
||||
MatrixStack projection_stack;
|
||||
|
||||
bool dirty;
|
||||
|
||||
/* TODO: cache of derived matrices (Normal, MVP, inverse MVP, etc)
|
||||
* generate as needed for shaders, invalidate when original matrices change
|
||||
*
|
||||
* TODO: separate Model from View transform? Batches/objects have model,
|
||||
* camera/eye has view & projection
|
||||
*/
|
||||
};
|
||||
|
||||
#define ModelViewStack Context::get()->matrix_state->model_view_stack
|
||||
#define ModelView ModelViewStack.stack[ModelViewStack.top]
|
||||
|
||||
#define ProjectionStack Context::get()->matrix_state->projection_stack
|
||||
#define Projection ProjectionStack.stack[ProjectionStack.top]
|
||||
|
||||
GPUMatrixState *GPU_matrix_state_create()
|
||||
{
|
||||
#define MATRIX_4X4_IDENTITY \
|
||||
{ \
|
||||
{1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, \
|
||||
{0.0f, 0.0f, 0.0f, 1.0f} \
|
||||
}
|
||||
|
||||
GPUMatrixState *state = MEM_new_uninitialized<GPUMatrixState>(__func__);
|
||||
const MatrixStack identity_stack = {{MATRIX_4X4_IDENTITY}, 0};
|
||||
|
||||
state->model_view_stack = state->projection_stack = identity_stack;
|
||||
state->dirty = true;
|
||||
|
||||
#undef MATRIX_4X4_IDENTITY
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void GPU_matrix_state_discard(GPUMatrixState *state)
|
||||
{
|
||||
MEM_delete(state);
|
||||
}
|
||||
|
||||
static void gpu_matrix_state_active_set_dirty(bool value)
|
||||
{
|
||||
GPUMatrixState *state = Context::get()->matrix_state;
|
||||
state->dirty = value;
|
||||
}
|
||||
|
||||
void GPU_matrix_reset()
|
||||
{
|
||||
GPUMatrixState *state = Context::get()->matrix_state;
|
||||
state->model_view_stack.top = 0;
|
||||
state->projection_stack.top = 0;
|
||||
unit_m4(ModelView);
|
||||
unit_m4(Projection);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
#ifdef WITH_GPU_SAFETY
|
||||
|
||||
/* Check if matrix is numerically good */
|
||||
static void checkmat(cosnt float *m)
|
||||
{
|
||||
const int n = 16;
|
||||
for (int i = 0; i < n; i++) {
|
||||
# if _MSC_VER
|
||||
BLI_assert(_finite(m[i]));
|
||||
# else
|
||||
BLI_assert(!isinf(m[i]));
|
||||
# endif
|
||||
}
|
||||
}
|
||||
|
||||
# define CHECKMAT(m) checkmat((const float *)m)
|
||||
|
||||
#else
|
||||
|
||||
# define CHECKMAT(m)
|
||||
|
||||
#endif
|
||||
|
||||
void GPU_matrix_push()
|
||||
{
|
||||
BLI_assert(ModelViewStack.top + 1 < MATRIX_STACK_DEPTH);
|
||||
ModelViewStack.top++;
|
||||
copy_m4_m4(ModelView, ModelViewStack.stack[ModelViewStack.top - 1]);
|
||||
}
|
||||
|
||||
void GPU_matrix_pop()
|
||||
{
|
||||
BLI_assert(ModelViewStack.top > 0);
|
||||
ModelViewStack.top--;
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_push_projection()
|
||||
{
|
||||
BLI_assert(ProjectionStack.top + 1 < MATRIX_STACK_DEPTH);
|
||||
ProjectionStack.top++;
|
||||
copy_m4_m4(Projection, ProjectionStack.stack[ProjectionStack.top - 1]);
|
||||
}
|
||||
|
||||
void GPU_matrix_pop_projection()
|
||||
{
|
||||
BLI_assert(ProjectionStack.top > 0);
|
||||
ProjectionStack.top--;
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_set(const float m[4][4])
|
||||
{
|
||||
copy_m4_m4(ModelView, m);
|
||||
CHECKMAT(ModelView3D);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_identity_projection_set()
|
||||
{
|
||||
unit_m4(Projection);
|
||||
CHECKMAT(Projection3D);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_projection_set(const float m[4][4])
|
||||
{
|
||||
copy_m4_m4(Projection, m);
|
||||
CHECKMAT(Projection3D);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_identity_set()
|
||||
{
|
||||
unit_m4(ModelView);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_translate_2f(float x, float y)
|
||||
{
|
||||
Mat4 m;
|
||||
unit_m4(m);
|
||||
m[3][0] = x;
|
||||
m[3][1] = y;
|
||||
GPU_matrix_mul(m);
|
||||
}
|
||||
|
||||
void GPU_matrix_translate_2fv(const float vec[2])
|
||||
{
|
||||
GPU_matrix_translate_2f(vec[0], vec[1]);
|
||||
}
|
||||
|
||||
void GPU_matrix_translate_3f(float x, float y, float z)
|
||||
{
|
||||
#if 1
|
||||
translate_m4(ModelView, x, y, z);
|
||||
CHECKMAT(ModelView);
|
||||
#else /* above works well in early testing, below is generic version */
|
||||
Mat4 m;
|
||||
unit_m4(m);
|
||||
m[3][0] = x;
|
||||
m[3][1] = y;
|
||||
m[3][2] = z;
|
||||
GPU_matrix_mul(m);
|
||||
#endif
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_translate_3fv(const float vec[3])
|
||||
{
|
||||
GPU_matrix_translate_3f(vec[0], vec[1], vec[2]);
|
||||
}
|
||||
|
||||
void GPU_matrix_scale_1f(float factor)
|
||||
{
|
||||
Mat4 m;
|
||||
scale_m4_fl(m, factor);
|
||||
GPU_matrix_mul(m);
|
||||
}
|
||||
|
||||
void GPU_matrix_scale_2f(float x, float y)
|
||||
{
|
||||
Mat4 m = {{0.0f}};
|
||||
m[0][0] = x;
|
||||
m[1][1] = y;
|
||||
m[2][2] = 1.0f;
|
||||
m[3][3] = 1.0f;
|
||||
GPU_matrix_mul(m);
|
||||
}
|
||||
|
||||
void GPU_matrix_scale_2fv(const float vec[2])
|
||||
{
|
||||
GPU_matrix_scale_2f(vec[0], vec[1]);
|
||||
}
|
||||
|
||||
void GPU_matrix_scale_3f(float x, float y, float z)
|
||||
{
|
||||
Mat4 m = {{0.0f}};
|
||||
m[0][0] = x;
|
||||
m[1][1] = y;
|
||||
m[2][2] = z;
|
||||
m[3][3] = 1.0f;
|
||||
GPU_matrix_mul(m);
|
||||
}
|
||||
|
||||
void GPU_matrix_scale_3fv(const float vec[3])
|
||||
{
|
||||
GPU_matrix_scale_3f(vec[0], vec[1], vec[2]);
|
||||
}
|
||||
|
||||
void GPU_matrix_mul(const float m[4][4])
|
||||
{
|
||||
mul_m4_m4_post(ModelView, m);
|
||||
CHECKMAT(ModelView);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_rotate_2d(float deg)
|
||||
{
|
||||
/* essentially RotateAxis('Z')
|
||||
* TODO: simpler math for 2D case
|
||||
*/
|
||||
rotate_m4(ModelView, 'Z', DEG2RADF(deg));
|
||||
}
|
||||
|
||||
void GPU_matrix_rotate_3f(float deg, float x, float y, float z)
|
||||
{
|
||||
const float axis[3] = {x, y, z};
|
||||
GPU_matrix_rotate_3fv(deg, axis);
|
||||
}
|
||||
|
||||
void GPU_matrix_rotate_3fv(float deg, const float axis[3])
|
||||
{
|
||||
Mat4 m;
|
||||
axis_angle_to_mat4(m, axis, DEG2RADF(deg));
|
||||
GPU_matrix_mul(m);
|
||||
}
|
||||
|
||||
void GPU_matrix_rotate_axis(float deg, char axis)
|
||||
{
|
||||
/* rotate_m4 works in place */
|
||||
rotate_m4(ModelView, axis, DEG2RADF(deg));
|
||||
CHECKMAT(ModelView);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
static void mat4_ortho_set(
|
||||
float m[4][4], float left, float right, float bottom, float top, float near, float far)
|
||||
{
|
||||
m[0][0] = 2.0f / (right - left);
|
||||
m[1][0] = 0.0f;
|
||||
m[2][0] = 0.0f;
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
|
||||
m[0][1] = 0.0f;
|
||||
m[1][1] = 2.0f / (top - bottom);
|
||||
m[2][1] = 0.0f;
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
|
||||
m[0][2] = 0.0f;
|
||||
m[1][2] = 0.0f;
|
||||
m[2][2] = -2.0f / (far - near);
|
||||
m[3][2] = -(far + near) / (far - near);
|
||||
|
||||
m[0][3] = 0.0f;
|
||||
m[1][3] = 0.0f;
|
||||
m[2][3] = 0.0f;
|
||||
m[3][3] = 1.0f;
|
||||
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
static void mat4_frustum_set(
|
||||
float m[4][4], float left, float right, float bottom, float top, float near, float far)
|
||||
{
|
||||
m[0][0] = 2.0f * near / (right - left);
|
||||
m[1][0] = 0.0f;
|
||||
m[2][0] = (right + left) / (right - left);
|
||||
m[3][0] = 0.0f;
|
||||
|
||||
m[0][1] = 0.0f;
|
||||
m[1][1] = 2.0f * near / (top - bottom);
|
||||
m[2][1] = (top + bottom) / (top - bottom);
|
||||
m[3][1] = 0.0f;
|
||||
|
||||
m[0][2] = 0.0f;
|
||||
m[1][2] = 0.0f;
|
||||
m[2][2] = -(far + near) / (far - near);
|
||||
m[3][2] = -2.0f * far * near / (far - near);
|
||||
|
||||
m[0][3] = 0.0f;
|
||||
m[1][3] = 0.0f;
|
||||
m[2][3] = -1.0f;
|
||||
m[3][3] = 0.0f;
|
||||
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3])
|
||||
{
|
||||
/* This function is loosely based on Mesa implementation.
|
||||
*
|
||||
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
|
||||
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice including the dates of first publication and
|
||||
* either this permission notice or a reference to
|
||||
* http://oss.sgi.com/projects/FreeB/
|
||||
* shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the name of Silicon Graphics, Inc.
|
||||
* shall not be used in advertising or otherwise to promote the sale, use or
|
||||
* other dealings in this Software without prior written authorization from
|
||||
* Silicon Graphics, Inc.
|
||||
*/
|
||||
float side[3];
|
||||
|
||||
normalize_v3(lookdir);
|
||||
|
||||
cross_v3_v3v3(side, lookdir, camup);
|
||||
|
||||
normalize_v3(side);
|
||||
|
||||
cross_v3_v3v3(camup, side, lookdir);
|
||||
|
||||
m[0][0] = side[0];
|
||||
m[1][0] = side[1];
|
||||
m[2][0] = side[2];
|
||||
m[3][0] = 0.0f;
|
||||
|
||||
m[0][1] = camup[0];
|
||||
m[1][1] = camup[1];
|
||||
m[2][1] = camup[2];
|
||||
m[3][1] = 0.0f;
|
||||
|
||||
m[0][2] = -lookdir[0];
|
||||
m[1][2] = -lookdir[1];
|
||||
m[2][2] = -lookdir[2];
|
||||
m[3][2] = 0.0f;
|
||||
|
||||
m[0][3] = 0.0f;
|
||||
m[1][3] = 0.0f;
|
||||
m[2][3] = 0.0f;
|
||||
m[3][3] = 1.0f;
|
||||
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far)
|
||||
{
|
||||
mat4_ortho_set(Projection, left, right, bottom, top, near, far);
|
||||
CHECKMAT(Projection);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_ortho_set_z(float near, float far)
|
||||
{
|
||||
CHECKMAT(Projection);
|
||||
Projection[2][2] = -2.0f / (far - near);
|
||||
Projection[3][2] = -(far + near) / (far - near);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top)
|
||||
{
|
||||
Mat4 m;
|
||||
mat4_ortho_set(m, left, right, bottom, top, -1.0f, 1.0f);
|
||||
CHECKMAT(Projection2D);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_frustum_set(
|
||||
float left, float right, float bottom, float top, float near, float far)
|
||||
{
|
||||
mat4_frustum_set(Projection, left, right, bottom, top, near, far);
|
||||
CHECKMAT(Projection);
|
||||
gpu_matrix_state_active_set_dirty(true);
|
||||
}
|
||||
|
||||
void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far)
|
||||
{
|
||||
float half_height = tanf(fovy * float(M_PI / 360.0)) * near;
|
||||
float half_width = half_height * aspect;
|
||||
GPU_matrix_frustum_set(-half_width, +half_width, -half_height, +half_height, near, far);
|
||||
}
|
||||
|
||||
void GPU_matrix_look_at(float eyeX,
|
||||
float eyeY,
|
||||
float eyeZ,
|
||||
float centerX,
|
||||
float centerY,
|
||||
float centerZ,
|
||||
float upX,
|
||||
float upY,
|
||||
float upZ)
|
||||
{
|
||||
Mat4 cm;
|
||||
float lookdir[3];
|
||||
float camup[3] = {upX, upY, upZ};
|
||||
|
||||
lookdir[0] = centerX - eyeX;
|
||||
lookdir[1] = centerY - eyeY;
|
||||
lookdir[2] = centerZ - eyeZ;
|
||||
|
||||
mat4_look_from_origin(cm, lookdir, camup);
|
||||
|
||||
GPU_matrix_mul(cm);
|
||||
GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ);
|
||||
}
|
||||
|
||||
void GPU_matrix_project_3fv(const float world[3],
|
||||
const float model[4][4],
|
||||
const float proj[4][4],
|
||||
const int view[4],
|
||||
float r_win[3])
|
||||
{
|
||||
float v[4];
|
||||
|
||||
mul_v4_m4v3(v, model, world);
|
||||
mul_m4_v4(proj, v);
|
||||
|
||||
if (v[3] != 0.0f) {
|
||||
mul_v3_fl(v, 1.0f / v[3]);
|
||||
}
|
||||
|
||||
r_win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f;
|
||||
r_win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f;
|
||||
r_win[2] = (v[2] + 1) * 0.5f;
|
||||
}
|
||||
|
||||
void GPU_matrix_project_2fv(const float world[3],
|
||||
const float model[4][4],
|
||||
const float proj[4][4],
|
||||
const int view[4],
|
||||
float r_win[2])
|
||||
{
|
||||
float v[4];
|
||||
|
||||
mul_v4_m4v3(v, model, world);
|
||||
mul_m4_v4(proj, v);
|
||||
|
||||
if (v[3] != 0.0f) {
|
||||
mul_v2_fl(v, 1.0f / v[3]);
|
||||
}
|
||||
|
||||
r_win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f;
|
||||
r_win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f;
|
||||
}
|
||||
|
||||
bool GPU_matrix_unproject_3fv(const float win[3],
|
||||
const float model_inverted[4][4],
|
||||
const float proj[4][4],
|
||||
const int view[4],
|
||||
float r_world[3])
|
||||
{
|
||||
zero_v3(r_world);
|
||||
const float in[3] = {
|
||||
2 * ((win[0] - view[0]) / view[2]) - 1.0f,
|
||||
2 * ((win[1] - view[1]) / view[3]) - 1.0f,
|
||||
2 * win[2] - 1.0f,
|
||||
};
|
||||
|
||||
/**
|
||||
* The same result could be obtained as follows:
|
||||
*
|
||||
* \code{.c}
|
||||
* float projinv[4][4];
|
||||
* invert_m4_m4(projinv, projview);
|
||||
* copy_v3_v3(r_world, in);
|
||||
* mul_project_m4_v3(projinv, r_world);
|
||||
* \endcode
|
||||
*
|
||||
* But that solution loses much precision.
|
||||
* Therefore, get the same result without inverting the project view matrix.
|
||||
*/
|
||||
|
||||
float out[3];
|
||||
const bool is_persp = proj[3][3] == 0.0f;
|
||||
if (is_persp) {
|
||||
out[2] = proj[3][2] / (proj[2][2] + in[2]);
|
||||
if (isinf(out[2])) {
|
||||
out[2] = FLT_MAX;
|
||||
}
|
||||
out[0] = out[2] * ((proj[2][0] + in[0]) / proj[0][0]);
|
||||
out[1] = out[2] * ((proj[2][1] + in[1]) / proj[1][1]);
|
||||
out[2] *= -1;
|
||||
}
|
||||
else {
|
||||
out[0] = (-proj[3][0] + in[0]) / proj[0][0];
|
||||
out[1] = (-proj[3][1] + in[1]) / proj[1][1];
|
||||
out[2] = (-proj[3][2] + in[2]) / proj[2][2];
|
||||
}
|
||||
|
||||
if (!is_finite_v3(out)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mul_v3_m4v3(r_world, model_inverted, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
const float (*GPU_matrix_model_view_get(float m[4][4]))[4]
|
||||
{
|
||||
if (m) {
|
||||
copy_m4_m4(m, ModelView);
|
||||
return m;
|
||||
}
|
||||
|
||||
return ModelView;
|
||||
}
|
||||
|
||||
const float (*GPU_matrix_projection_get(float m[4][4]))[4]
|
||||
{
|
||||
if (m) {
|
||||
copy_m4_m4(m, Projection);
|
||||
return m;
|
||||
}
|
||||
|
||||
return Projection;
|
||||
}
|
||||
|
||||
const float (*GPU_matrix_model_view_projection_get(float m[4][4]))[4]
|
||||
{
|
||||
if (m == nullptr) {
|
||||
static Mat4 temp;
|
||||
m = temp;
|
||||
}
|
||||
|
||||
mul_m4_m4m4(m, Projection, ModelView);
|
||||
return m;
|
||||
}
|
||||
|
||||
const float (*GPU_matrix_normal_get(float m[3][3]))[3]
|
||||
{
|
||||
if (m == nullptr) {
|
||||
static Mat3 temp3;
|
||||
m = temp3;
|
||||
}
|
||||
|
||||
copy_m3_m4(m, GPU_matrix_model_view_get(nullptr));
|
||||
|
||||
invert_m3(m);
|
||||
transpose_m3(m);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3]
|
||||
{
|
||||
if (m == nullptr) {
|
||||
static Mat3 temp3;
|
||||
m = temp3;
|
||||
}
|
||||
|
||||
GPU_matrix_normal_get(m);
|
||||
invert_m3(m);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void GPU_matrix_bind(gpu::Shader *shader)
|
||||
{
|
||||
/* set uniform values to matrix stack values
|
||||
* call this before a draw call if desired matrices are dirty
|
||||
* call glUseProgram before this, as glUniform expects program to be bound
|
||||
*/
|
||||
int32_t MV = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW);
|
||||
int32_t P = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION);
|
||||
int32_t MVP = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MVP);
|
||||
|
||||
int32_t N = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_NORMAL);
|
||||
int32_t MV_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV);
|
||||
int32_t P_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION_INV);
|
||||
|
||||
if (MV != -1) {
|
||||
GPU_shader_uniform_float_ex(
|
||||
shader, MV, 16, 1, reinterpret_cast<const float *>(GPU_matrix_model_view_get(nullptr)));
|
||||
}
|
||||
if (P != -1) {
|
||||
GPU_shader_uniform_float_ex(
|
||||
shader, P, 16, 1, reinterpret_cast<const float *>(GPU_matrix_projection_get(nullptr)));
|
||||
}
|
||||
if (MVP != -1) {
|
||||
GPU_shader_uniform_float_ex(
|
||||
shader,
|
||||
MVP,
|
||||
16,
|
||||
1,
|
||||
reinterpret_cast<const float *>(GPU_matrix_model_view_projection_get(nullptr)));
|
||||
}
|
||||
if (N != -1) {
|
||||
GPU_shader_uniform_float_ex(
|
||||
shader, N, 9, 1, reinterpret_cast<const float *>(GPU_matrix_normal_get(nullptr)));
|
||||
}
|
||||
if (MV_inv != -1) {
|
||||
Mat4 m;
|
||||
GPU_matrix_model_view_get(m);
|
||||
invert_m4(m);
|
||||
GPU_shader_uniform_float_ex(shader, MV_inv, 16, 1, reinterpret_cast<const float *>(m));
|
||||
}
|
||||
if (P_inv != -1) {
|
||||
Mat4 m;
|
||||
GPU_matrix_projection_get(m);
|
||||
invert_m4(m);
|
||||
GPU_shader_uniform_float_ex(shader, P_inv, 16, 1, reinterpret_cast<const float *>(m));
|
||||
}
|
||||
|
||||
gpu_matrix_state_active_set_dirty(false);
|
||||
}
|
||||
|
||||
bool GPU_matrix_dirty_get()
|
||||
{
|
||||
GPUMatrixState *state = Context::get()->matrix_state;
|
||||
return state->dirty;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Python API Helpers
|
||||
* \{ */
|
||||
|
||||
BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mismatch");
|
||||
|
||||
/* Return int since caller is may subtract. */
|
||||
|
||||
int GPU_matrix_stack_level_get_model_view()
|
||||
{
|
||||
GPUMatrixState *state = Context::get()->matrix_state;
|
||||
return int(state->model_view_stack.top);
|
||||
}
|
||||
|
||||
int GPU_matrix_stack_level_get_projection()
|
||||
{
|
||||
GPUMatrixState *state = Context::get()->matrix_state;
|
||||
return int(state->projection_stack.top);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Polygon Offset Hack
|
||||
*
|
||||
* Workaround the fact that polygon-offset is implementation dependent.
|
||||
* We modify the projection matrix \a winmat in order to change the final depth a tiny amount.
|
||||
* \{ */
|
||||
|
||||
float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float dist)
|
||||
{
|
||||
/* Seems like we have a factor of 2 more offset than 2.79 for some reason. Correct for this. */
|
||||
dist *= 0.5f;
|
||||
|
||||
if (winmat[3][3] > 0.5f) {
|
||||
#if 1
|
||||
return 0.00001f * dist * viewdist; // ortho tweaking
|
||||
#else
|
||||
static float depth_fac = 0.0f;
|
||||
if (depth_fac == 0.0f) {
|
||||
/* Hard-code for 24 bit precision. */
|
||||
int depthbits = 24;
|
||||
depth_fac = 1.0f / float((1 << depthbits) - 1);
|
||||
}
|
||||
ofs = (-1.0 / winmat[2][2]) * dist * depth_fac;
|
||||
|
||||
UNUSED_VARS(viewdist);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This adjustment effectively results in reducing the Z value by 0.25%.
|
||||
*
|
||||
* winmat[4][3] actually evaluates to `-2 * far * near / (far - near)`,
|
||||
* is very close to -0.2 with default clip range,
|
||||
* and is used as the coefficient multiplied by `w / z`,
|
||||
* thus controlling the z dependent part of the depth value.
|
||||
*/
|
||||
return winmat[3][2] * -0.0025f * dist;
|
||||
}
|
||||
|
||||
void GPU_polygon_offset(float viewdist, float dist)
|
||||
{
|
||||
static float winmat[4][4], offset = 0.0f;
|
||||
|
||||
if (dist != 0.0f) {
|
||||
/* hack below is to mimic polygon offset */
|
||||
GPU_matrix_projection_get(winmat);
|
||||
|
||||
/* dist is from camera to center point */
|
||||
|
||||
float ofs = GPU_polygon_offset_calc(winmat, viewdist, dist);
|
||||
|
||||
winmat[3][2] -= ofs;
|
||||
offset += ofs;
|
||||
}
|
||||
else {
|
||||
winmat[3][2] += offset;
|
||||
offset = 0.0;
|
||||
}
|
||||
|
||||
GPU_matrix_projection_set(winmat);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUMatrixState;
|
||||
|
||||
GPUMatrixState *GPU_matrix_state_create();
|
||||
void GPU_matrix_state_discard(GPUMatrixState *state);
|
||||
|
||||
} // namespace blender
|
||||
1147
blender-5.2.0/source/blender/gpu/intern/gpu_node_graph.cc
Normal file
1147
blender-5.2.0/source/blender/gpu/intern/gpu_node_graph.cc
Normal file
File diff suppressed because it is too large
Load Diff
242
blender-5.2.0/source/blender/gpu/intern/gpu_node_graph.hh
Normal file
242
blender-5.2.0/source/blender/gpu/intern/gpu_node_graph.hh
Normal file
@@ -0,0 +1,242 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Intermediate node graph for generating GLSL shaders.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
|
||||
#include "GPU_material.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUNode;
|
||||
struct GPUOutput;
|
||||
struct GPUInput;
|
||||
|
||||
enum GPUDataSource {
|
||||
GPU_SOURCE_OUTPUT,
|
||||
GPU_SOURCE_CONSTANT,
|
||||
GPU_SOURCE_UNIFORM,
|
||||
GPU_SOURCE_ATTR,
|
||||
GPU_SOURCE_UNIFORM_ATTR,
|
||||
GPU_SOURCE_LAYER_ATTR,
|
||||
GPU_SOURCE_STRUCT,
|
||||
GPU_SOURCE_TEX,
|
||||
GPU_SOURCE_TEX_TILED_MAPPING,
|
||||
GPU_SOURCE_FUNCTION_CALL,
|
||||
GPU_SOURCE_CRYPTOMATTE,
|
||||
};
|
||||
|
||||
enum GPUNodeLinkType {
|
||||
GPU_NODE_LINK_NONE = 0,
|
||||
GPU_NODE_LINK_ATTR,
|
||||
GPU_NODE_LINK_UNIFORM_ATTR,
|
||||
GPU_NODE_LINK_LAYER_ATTR,
|
||||
GPU_NODE_LINK_COLORBAND,
|
||||
GPU_NODE_LINK_CONSTANT,
|
||||
GPU_NODE_LINK_IMAGE,
|
||||
GPU_NODE_LINK_IMAGE_TILED,
|
||||
GPU_NODE_LINK_IMAGE_TILED_MAPPING,
|
||||
GPU_NODE_LINK_IMAGE_SKY,
|
||||
GPU_NODE_LINK_OUTPUT,
|
||||
GPU_NODE_LINK_UNIFORM,
|
||||
GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN,
|
||||
};
|
||||
|
||||
enum GPUNodeTag {
|
||||
GPU_NODE_TAG_NONE = 0,
|
||||
GPU_NODE_TAG_SURFACE = (1 << 0),
|
||||
GPU_NODE_TAG_VOLUME = (1 << 1),
|
||||
GPU_NODE_TAG_DISPLACEMENT = (1 << 2),
|
||||
GPU_NODE_TAG_THICKNESS = (1 << 3),
|
||||
GPU_NODE_TAG_AOV = (1 << 4),
|
||||
GPU_NODE_TAG_FUNCTION = (1 << 5),
|
||||
GPU_NODE_TAG_COMPOSITOR = (1 << 6),
|
||||
};
|
||||
|
||||
ENUM_OPERATORS(GPUNodeTag)
|
||||
|
||||
struct GPUNode {
|
||||
GPUNode *next, *prev;
|
||||
|
||||
const char *name;
|
||||
|
||||
/* Internal flag to mark nodes during pruning */
|
||||
GPUNodeTag tag;
|
||||
|
||||
ListBaseT<GPUInput> inputs;
|
||||
ListBaseT<GPUOutput> outputs;
|
||||
|
||||
/* Zones. */
|
||||
int zone_index;
|
||||
bool is_zone_end;
|
||||
};
|
||||
|
||||
struct GPUNodeLink {
|
||||
GPUNodeStack *socket;
|
||||
|
||||
GPUNodeLinkType link_type;
|
||||
int users; /* Refcount */
|
||||
|
||||
union {
|
||||
/* GPU_NODE_LINK_CONSTANT | GPU_NODE_LINK_UNIFORM */
|
||||
const float *data;
|
||||
/* GPU_NODE_LINK_COLORBAND */
|
||||
gpu::Texture **colorband;
|
||||
/* GPU_NODE_LINK_OUTPUT */
|
||||
GPUOutput *output;
|
||||
/* GPU_NODE_LINK_ATTR */
|
||||
GPUMaterialAttribute *attr;
|
||||
/* GPU_NODE_LINK_UNIFORM_ATTR */
|
||||
GPUUniformAttr *uniform_attr;
|
||||
/* GPU_NODE_LINK_LAYER_ATTR */
|
||||
GPULayerAttr *layer_attr;
|
||||
/* GPU_NODE_LINK_IMAGE_BLENDER */
|
||||
GPUMaterialTexture *texture;
|
||||
/* GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN */
|
||||
struct {
|
||||
const char *function_name;
|
||||
float filter_width;
|
||||
} differentiate_float;
|
||||
};
|
||||
};
|
||||
|
||||
struct GPUOutput {
|
||||
GPUOutput *next, *prev;
|
||||
|
||||
GPUNode *node;
|
||||
GPUType type; /* data type = length of vector/matrix */
|
||||
GPUNodeLink *link; /* output link */
|
||||
int id; /* unique id as created by code generator */
|
||||
|
||||
/* True for Zone Items. */
|
||||
bool is_zone_io;
|
||||
/* This variable is shared with other socket/s and doesn't need to be declared. */
|
||||
bool is_duplicate;
|
||||
};
|
||||
|
||||
struct GPUInput {
|
||||
GPUInput *next, *prev;
|
||||
|
||||
GPUNode *node;
|
||||
GPUType type; /* data-type. */
|
||||
GPUNodeLink *link;
|
||||
int id; /* unique id as created by code generator */
|
||||
|
||||
GPUDataSource source; /* data source */
|
||||
|
||||
/* Content based on GPUDataSource */
|
||||
union {
|
||||
/* GPU_SOURCE_CONSTANT | GPU_SOURCE_UNIFORM */
|
||||
float vec[16]; /* vector data */
|
||||
/* GPU_SOURCE_TEX | GPU_SOURCE_TEX_TILED_MAPPING */
|
||||
GPUMaterialTexture *texture;
|
||||
/* GPU_SOURCE_ATTR */
|
||||
GPUMaterialAttribute *attr;
|
||||
/* GPU_SOURCE_UNIFORM_ATTR */
|
||||
GPUUniformAttr *uniform_attr;
|
||||
/* GPU_SOURCE_LAYER_ATTR */
|
||||
GPULayerAttr *layer_attr;
|
||||
/* GPU_SOURCE_FUNCTION_CALL */
|
||||
char function_call[64];
|
||||
};
|
||||
|
||||
/* True for Zone Items. */
|
||||
bool is_zone_io;
|
||||
/* This variable is shared with other socket/s and doesn't need to be declared. */
|
||||
bool is_duplicate;
|
||||
};
|
||||
|
||||
struct GPUNodeGraphOutputLink {
|
||||
GPUNodeGraphOutputLink *next, *prev;
|
||||
int hash;
|
||||
GPUNodeLink *outlink;
|
||||
};
|
||||
|
||||
struct GPUNodeGraphFunctionLink {
|
||||
GPUNodeGraphFunctionLink *next, *prev;
|
||||
char name[16];
|
||||
GPUNodeLink *outlink;
|
||||
};
|
||||
|
||||
struct GPUNodeGraph {
|
||||
/* Nodes */
|
||||
ListBaseT<GPUNode> nodes;
|
||||
|
||||
/* Main Outputs. */
|
||||
GPUNodeLink *outlink_surface;
|
||||
GPUNodeLink *outlink_volume;
|
||||
GPUNodeLink *outlink_displacement;
|
||||
GPUNodeLink *outlink_thickness;
|
||||
/* List of GPUNodeGraphOutputLink */
|
||||
ListBaseT<GPUNodeGraphOutputLink> outlink_aovs;
|
||||
/* List of GPUNodeGraphFunctionLink */
|
||||
ListBaseT<GPUNodeGraphFunctionLink> material_functions;
|
||||
/* List of GPUNodeGraphOutputLink */
|
||||
ListBaseT<GPUNodeGraphOutputLink> outlink_compositor;
|
||||
|
||||
/* Requested attributes and textures. */
|
||||
ListBaseT<GPUMaterialAttribute> attributes;
|
||||
ListBaseT<GPUMaterialTexture> textures;
|
||||
|
||||
/* The list of uniform attributes. */
|
||||
GPUUniformAttrList uniform_attrs;
|
||||
|
||||
/* The list of layer attributes. */
|
||||
ListBaseT<GPULayerAttr> layer_attrs;
|
||||
};
|
||||
|
||||
/* Node Graph */
|
||||
|
||||
void gpu_nodes_tag(GPUNodeGraph *graph, GPUNodeLink *link_start, GPUNodeTag tag);
|
||||
void gpu_node_graph_prune_unused(GPUNodeGraph *graph);
|
||||
void gpu_node_graph_finalize_uniform_attrs(GPUNodeGraph *graph);
|
||||
|
||||
/**
|
||||
* Optimize node graph for optimized material shader path.
|
||||
* Once the base material has been generated, we can modify the shader
|
||||
* node graph to create one which will produce an optimally performing shader.
|
||||
* This currently involves baking uniform data into constant data to enable
|
||||
* aggressive constant folding by the compiler in order to reduce complexity and
|
||||
* shader core memory pressure.
|
||||
*
|
||||
* NOTE: Graph optimizations will produce a shader which needs to be re-compiled
|
||||
* more frequently, however, the default material pass will always exist to fall
|
||||
* back on. */
|
||||
void gpu_node_graph_optimize(GPUNodeGraph *graph);
|
||||
|
||||
/**
|
||||
* Free intermediate node graph.
|
||||
*/
|
||||
void gpu_node_graph_free_nodes(GPUNodeGraph *graph);
|
||||
/**
|
||||
* Free both node graph and requested attributes and textures.
|
||||
*/
|
||||
void gpu_node_graph_free(GPUNodeGraph *graph);
|
||||
|
||||
/* Material calls */
|
||||
|
||||
GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material);
|
||||
/**
|
||||
* Returns the address of the future pointer to coba_tex.
|
||||
*/
|
||||
gpu::Texture **gpu_material_ramp_texture_row_set(GPUMaterial *mat,
|
||||
int size,
|
||||
const float *pixels,
|
||||
float *r_row);
|
||||
/**
|
||||
* Returns the address of the future pointer to sky_tex
|
||||
*/
|
||||
gpu::Texture **gpu_material_sky_texture_layer_set(
|
||||
GPUMaterial *mat, int width, int height, const float *pixels, float *row);
|
||||
|
||||
} // namespace blender
|
||||
405
blender-5.2.0/source/blender/gpu/intern/gpu_pass.cc
Normal file
405
blender-5.2.0/source/blender/gpu/intern/gpu_pass.cc
Normal file
@@ -0,0 +1,405 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Convert material node-trees to GLSL.
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_time.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_pass.hh"
|
||||
#include "GPU_vertex_format.hh"
|
||||
#include "gpu_codegen.hh"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
static bool gpu_pass_validate(GPUCodegenCreateInfo *create_info);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUPass
|
||||
* \{ */
|
||||
|
||||
struct GPUPass {
|
||||
static inline std::atomic<uint64_t> compilation_counts = 0;
|
||||
|
||||
GPUCodegenCreateInfo *create_info = nullptr;
|
||||
AsyncCompilationHandle compilation_handle = 0;
|
||||
std::atomic<gpu::Shader *> shader = nullptr;
|
||||
std::atomic<GPUPassStatus> status = GPU_PASS_QUEUED;
|
||||
/* Orphaned GPUPasses gets freed by the garbage collector. */
|
||||
std::atomic<int> refcount = 1;
|
||||
double creation_timestamp = 0.0f;
|
||||
/* The last time the refcount was greater than 0. */
|
||||
double gc_timestamp = 0.0f;
|
||||
|
||||
uint64_t compilation_timestamp = 0;
|
||||
|
||||
/** Hint that an optimized variant of this pass should be created.
|
||||
* Based on a complexity heuristic from pass code generation. */
|
||||
bool should_optimize = false;
|
||||
bool is_optimization_pass = false;
|
||||
|
||||
/* Number of seconds after creation required before compiling an optimization pass. */
|
||||
static constexpr float optimization_delay = 10.0f;
|
||||
|
||||
GPUPass(GPUCodegenCreateInfo *info,
|
||||
bool deferred_compilation,
|
||||
bool is_optimization_pass,
|
||||
bool should_optimize)
|
||||
: create_info(info),
|
||||
creation_timestamp(BLI_time_now_seconds()),
|
||||
should_optimize(should_optimize),
|
||||
is_optimization_pass(is_optimization_pass)
|
||||
{
|
||||
BLI_assert(!is_optimization_pass || !should_optimize);
|
||||
if (is_optimization_pass && deferred_compilation) {
|
||||
// Defer until all non optimization passes are compiled.
|
||||
return;
|
||||
}
|
||||
|
||||
GPUShaderCreateInfo *base_info = reinterpret_cast<GPUShaderCreateInfo *>(create_info);
|
||||
|
||||
if (deferred_compilation) {
|
||||
compilation_handle = GPU_shader_async_compilation(base_info, compilation_priority());
|
||||
}
|
||||
else {
|
||||
shader = GPU_shader_create_from_info(base_info);
|
||||
finalize_compilation();
|
||||
}
|
||||
}
|
||||
|
||||
~GPUPass()
|
||||
{
|
||||
if (compilation_handle) {
|
||||
GPU_shader_async_compilation_cancel(compilation_handle);
|
||||
}
|
||||
else {
|
||||
BLI_assert(create_info == nullptr || (is_optimization_pass && status == GPU_PASS_QUEUED));
|
||||
}
|
||||
MEM_delete(create_info);
|
||||
GPU_SHADER_FREE_SAFE(shader);
|
||||
}
|
||||
|
||||
CompilationPriority compilation_priority()
|
||||
{
|
||||
return is_optimization_pass ? CompilationPriority::Low : CompilationPriority::Medium;
|
||||
}
|
||||
|
||||
void finalize_compilation()
|
||||
{
|
||||
BLI_assert_msg(create_info, "GPUPass::finalize_compilation() called more than once.");
|
||||
|
||||
if (compilation_handle) {
|
||||
shader = GPU_shader_async_compilation_finalize(compilation_handle);
|
||||
}
|
||||
|
||||
compilation_timestamp = ++compilation_counts;
|
||||
|
||||
if (!shader && !gpu_pass_validate(create_info)) {
|
||||
fprintf(stderr, "gpu::Shader: error: too many samplers in shader.\n");
|
||||
}
|
||||
|
||||
status = shader ? GPU_PASS_SUCCESS : GPU_PASS_FAILED;
|
||||
|
||||
MEM_delete(create_info);
|
||||
create_info = nullptr;
|
||||
}
|
||||
|
||||
void update(double timestamp)
|
||||
{
|
||||
update_compilation(timestamp);
|
||||
update_gc_timestamp(timestamp);
|
||||
}
|
||||
|
||||
void update_compilation(double timestamp)
|
||||
{
|
||||
if (compilation_handle) {
|
||||
if (GPU_shader_async_compilation_is_ready(compilation_handle)) {
|
||||
finalize_compilation();
|
||||
}
|
||||
}
|
||||
else if (status == GPU_PASS_QUEUED && refcount > 0 &&
|
||||
((creation_timestamp + optimization_delay) <= timestamp))
|
||||
{
|
||||
BLI_assert(is_optimization_pass);
|
||||
GPUShaderCreateInfo *base_info = reinterpret_cast<GPUShaderCreateInfo *>(create_info);
|
||||
compilation_handle = GPU_shader_async_compilation(base_info, compilation_priority());
|
||||
}
|
||||
}
|
||||
|
||||
void update_gc_timestamp(double timestamp)
|
||||
{
|
||||
if (refcount != 0 || gc_timestamp == 0.0f) {
|
||||
gc_timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
bool should_gc(int gc_collect_rate, double timestamp)
|
||||
{
|
||||
BLI_assert(gc_timestamp != 0.0f);
|
||||
return !compilation_handle && status != GPU_PASS_FAILED &&
|
||||
(timestamp - gc_timestamp) >= gc_collect_rate;
|
||||
}
|
||||
};
|
||||
|
||||
GPUPassStatus GPU_pass_status(GPUPass *pass)
|
||||
{
|
||||
return pass->status;
|
||||
}
|
||||
|
||||
bool GPU_pass_should_optimize(GPUPass *pass)
|
||||
{
|
||||
/* Returns optimization heuristic prepared during
|
||||
* initial codegen.
|
||||
* NOTE: Only enabled on Metal, since it doesn't seem to yield any performance improvements for
|
||||
* other backends. */
|
||||
return (GPU_backend_get_type() == GPU_BACKEND_METAL) && pass->should_optimize;
|
||||
}
|
||||
|
||||
gpu::Shader *GPU_pass_shader_get(GPUPass *pass)
|
||||
{
|
||||
return pass->shader;
|
||||
}
|
||||
|
||||
void GPU_pass_acquire(GPUPass *pass)
|
||||
{
|
||||
int previous_refcount = pass->refcount++;
|
||||
UNUSED_VARS_NDEBUG(previous_refcount);
|
||||
BLI_assert(previous_refcount > 0);
|
||||
}
|
||||
|
||||
void GPU_pass_release(GPUPass *pass)
|
||||
{
|
||||
int previous_refcount = pass->refcount--;
|
||||
UNUSED_VARS_NDEBUG(previous_refcount);
|
||||
BLI_assert(previous_refcount > 0);
|
||||
}
|
||||
|
||||
uint64_t GPU_pass_global_compilation_count()
|
||||
{
|
||||
return GPUPass::compilation_counts;
|
||||
}
|
||||
|
||||
uint64_t GPU_pass_compilation_timestamp(GPUPass *pass)
|
||||
{
|
||||
return pass->compilation_timestamp;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUPass Cache
|
||||
*
|
||||
* Internal shader cache: This prevent the shader recompilation / stall when
|
||||
* using undo/redo AND also allows for GPUPass reuse if the Shader code is the
|
||||
* same for 2 different Materials. Unused GPUPasses are free by Garbage collection.
|
||||
* \{ */
|
||||
|
||||
class GPUPassCache {
|
||||
|
||||
/** Number of seconds with 0 users required before garbage collecting a pass. */
|
||||
static constexpr float gc_collect_rate_ = 60.0f;
|
||||
static constexpr float optimization_gc_collect_rate_ = 1.0f;
|
||||
|
||||
Map<uint32_t, std::unique_ptr<GPUPass>> passes_[GPU_MAT_ENGINE_MAX][2 /*is_optimization_pass*/];
|
||||
std::mutex mutex_;
|
||||
|
||||
public:
|
||||
~GPUPassCache()
|
||||
{
|
||||
/* Pause to prevent new compilations to start while we are cancelling them. */
|
||||
GPU_shader_compiler_pause();
|
||||
for (int i : IndexRange(GPU_MAT_ENGINE_MAX)) {
|
||||
passes_[i][0].clear();
|
||||
passes_[i][1].clear();
|
||||
}
|
||||
GPU_shader_compiler_resume();
|
||||
}
|
||||
|
||||
void add(eGPUMaterialEngine engine,
|
||||
GPUCodegen &codegen,
|
||||
bool deferred_compilation,
|
||||
bool is_optimization_pass)
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
|
||||
passes_[engine][is_optimization_pass].add(
|
||||
codegen.hash_get(),
|
||||
std::make_unique<GPUPass>(codegen.create_info,
|
||||
deferred_compilation,
|
||||
is_optimization_pass,
|
||||
codegen.should_optimize_heuristic()));
|
||||
};
|
||||
|
||||
GPUPass *get(eGPUMaterialEngine engine,
|
||||
size_t hash,
|
||||
bool allow_deferred,
|
||||
bool is_optimization_pass)
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
std::unique_ptr<GPUPass> *pass = passes_[engine][is_optimization_pass].lookup_ptr(hash);
|
||||
if (!allow_deferred && pass && pass->get()->status == GPU_PASS_QUEUED) {
|
||||
pass->get()->finalize_compilation();
|
||||
}
|
||||
return pass ? pass->get() : nullptr;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
|
||||
double timestamp = BLI_time_now_seconds();
|
||||
|
||||
/* Base Passes. */
|
||||
for (auto &engine_passes : passes_) {
|
||||
for (std::unique_ptr<GPUPass> &pass : engine_passes[false].values()) {
|
||||
pass->update(timestamp);
|
||||
}
|
||||
|
||||
engine_passes[false].remove_if(
|
||||
[&](auto item) { return item.value->should_gc(gc_collect_rate_, timestamp); });
|
||||
}
|
||||
|
||||
/* Optimization Passes */
|
||||
for (auto &engine_passes : passes_) {
|
||||
for (std::unique_ptr<GPUPass> &pass : engine_passes[true].values()) {
|
||||
pass->update(timestamp);
|
||||
}
|
||||
|
||||
engine_passes[true].remove_if([&](auto item) {
|
||||
return item.value->should_gc(optimization_gc_collect_rate_, timestamp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex &get_mutex()
|
||||
{
|
||||
return mutex_;
|
||||
}
|
||||
};
|
||||
|
||||
static GPUPassCache *g_cache = nullptr;
|
||||
|
||||
void GPU_pass_ensure_its_ready(GPUPass *pass)
|
||||
{
|
||||
if (pass->status == GPU_PASS_QUEUED) {
|
||||
std::lock_guard lock(g_cache->get_mutex());
|
||||
if (pass->status == GPU_PASS_QUEUED) {
|
||||
pass->finalize_compilation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_pass_cache_init()
|
||||
{
|
||||
g_cache = MEM_new<GPUPassCache>(__func__);
|
||||
}
|
||||
|
||||
void GPU_pass_cache_update()
|
||||
{
|
||||
g_cache->update();
|
||||
}
|
||||
|
||||
void GPU_pass_cache_wait_for_all()
|
||||
{
|
||||
GPU_shader_compiler_wait_for_all();
|
||||
g_cache->update();
|
||||
}
|
||||
|
||||
void GPU_pass_cache_free()
|
||||
{
|
||||
MEM_SAFE_DELETE(g_cache);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Compilation
|
||||
* \{ */
|
||||
|
||||
static bool gpu_pass_validate(GPUCodegenCreateInfo *create_info)
|
||||
{
|
||||
int samplers_len = 0;
|
||||
for (const ShaderCreateInfo::Resource &res : create_info->resources_get_all_()) {
|
||||
if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
|
||||
samplers_len++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate against GPU limit. */
|
||||
return samplers_len <= GPU_max_textures();
|
||||
}
|
||||
|
||||
GPUPass *GPU_generate_pass(GPUMaterial *material,
|
||||
GPUNodeGraph *graph,
|
||||
const char *debug_name,
|
||||
eGPUMaterialEngine engine,
|
||||
bool deferred_compilation,
|
||||
GPUCodegenCallbackFn finalize_source_cb,
|
||||
void *thunk,
|
||||
bool optimize_graph)
|
||||
{
|
||||
gpu_node_graph_prune_unused(graph);
|
||||
|
||||
/* If Optimize flag is passed in, we are generating an optimized
|
||||
* variant of the GPUMaterial's GPUPass. */
|
||||
if (optimize_graph) {
|
||||
gpu_node_graph_optimize(graph);
|
||||
}
|
||||
|
||||
/* Extract attributes before compiling so the generated VBOs are ready to accept the future
|
||||
* shader. */
|
||||
gpu_node_graph_finalize_uniform_attrs(graph);
|
||||
|
||||
GPUCodegen codegen(material, graph, debug_name);
|
||||
codegen.generate_graphs();
|
||||
codegen.generate_cryptomatte();
|
||||
|
||||
GPUPass *pass = nullptr;
|
||||
|
||||
if (!optimize_graph) {
|
||||
/* The optimized version of the shader should not re-generate a UBO.
|
||||
* The UBO will not be used for this variant. */
|
||||
codegen.generate_uniform_buffer();
|
||||
}
|
||||
|
||||
/* Cache lookup: Reuse shaders already compiled. */
|
||||
pass = g_cache->get(engine, codegen.hash_get(), deferred_compilation, optimize_graph);
|
||||
|
||||
if (pass) {
|
||||
pass->refcount++;
|
||||
return pass;
|
||||
}
|
||||
|
||||
/* The shader is not compiled, continue generating the shader strings. */
|
||||
codegen.generate_attribs();
|
||||
codegen.generate_resources();
|
||||
|
||||
/* Make engine add its own code and implement the generated functions. */
|
||||
finalize_source_cb(thunk, material, &codegen.output);
|
||||
|
||||
codegen.create_info->finalize();
|
||||
g_cache->add(engine, codegen, deferred_compilation, optimize_graph);
|
||||
codegen.create_info = nullptr;
|
||||
|
||||
return g_cache->get(engine, codegen.hash_get(), deferred_compilation, optimize_graph);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
206
blender-5.2.0/source/blender/gpu/intern/gpu_platform.cc
Normal file
206
blender-5.2.0/source/blender/gpu/intern/gpu_platform.cc
Normal file
@@ -0,0 +1,206 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Wrap OpenGL features such as textures, shaders and GLSL
|
||||
* with checks for drivers and GPU support.
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
#include "gpu_platform_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUPlatformGlobal
|
||||
* \{ */
|
||||
|
||||
namespace gpu {
|
||||
|
||||
GPUPlatformGlobal GPG;
|
||||
|
||||
static char *create_key(GPUSupportLevel support_level,
|
||||
const char *vendor,
|
||||
const char *renderer,
|
||||
const char *version)
|
||||
{
|
||||
DynStr *ds = BLI_dynstr_new();
|
||||
BLI_dynstr_appendf(ds, "{%s/%s/%s}=", vendor, renderer, version);
|
||||
if (support_level == GPU_SUPPORT_LEVEL_SUPPORTED) {
|
||||
BLI_dynstr_append(ds, "SUPPORTED");
|
||||
}
|
||||
else if (support_level == GPU_SUPPORT_LEVEL_LIMITED) {
|
||||
BLI_dynstr_append(ds, "LIMITED");
|
||||
}
|
||||
else {
|
||||
BLI_dynstr_append(ds, "UNSUPPORTED");
|
||||
}
|
||||
|
||||
char *support_key = BLI_dynstr_get_cstring(ds);
|
||||
BLI_dynstr_free(ds);
|
||||
BLI_string_replace_char(support_key, '\n', ' ');
|
||||
BLI_string_replace_char(support_key, '\r', ' ');
|
||||
return support_key;
|
||||
}
|
||||
|
||||
static char *create_gpu_name(const char *vendor, const char *renderer, const char *version)
|
||||
{
|
||||
DynStr *ds = BLI_dynstr_new();
|
||||
BLI_dynstr_appendf(ds, "%s %s %s", vendor, renderer, version);
|
||||
|
||||
char *gpu_name = BLI_dynstr_get_cstring(ds);
|
||||
BLI_dynstr_free(ds);
|
||||
BLI_string_replace_char(gpu_name, '\n', ' ');
|
||||
BLI_string_replace_char(gpu_name, '\r', ' ');
|
||||
return gpu_name;
|
||||
}
|
||||
|
||||
void GPUPlatformGlobal::init(GPUDeviceType gpu_device,
|
||||
GPUOSType os_type,
|
||||
GPUDriverType driver_type,
|
||||
GPUSupportLevel gpu_support_level,
|
||||
GPUBackendType backend,
|
||||
const char *vendor_str,
|
||||
const char *renderer_str,
|
||||
const char *version_str,
|
||||
GPUArchitectureType arch_type)
|
||||
{
|
||||
this->clear();
|
||||
|
||||
this->initialized = true;
|
||||
|
||||
this->device = gpu_device;
|
||||
this->os = os_type;
|
||||
this->driver = driver_type;
|
||||
this->support_level = gpu_support_level;
|
||||
|
||||
const char *vendor = vendor_str ? vendor_str : "UNKNOWN";
|
||||
const char *renderer = renderer_str ? renderer_str : "UNKNOWN";
|
||||
const char *version = version_str ? version_str : "UNKNOWN";
|
||||
|
||||
this->vendor = BLI_strdup(vendor);
|
||||
this->renderer = BLI_strdup(renderer);
|
||||
this->version = BLI_strdup(version);
|
||||
this->support_key = create_key(gpu_support_level, vendor, renderer, version);
|
||||
this->gpu_name = create_gpu_name(vendor, renderer, version);
|
||||
this->backend = backend;
|
||||
this->architecture_type = arch_type;
|
||||
}
|
||||
|
||||
void GPUPlatformGlobal::clear()
|
||||
{
|
||||
MEM_SAFE_DELETE(vendor);
|
||||
MEM_SAFE_DELETE(renderer);
|
||||
MEM_SAFE_DELETE(version);
|
||||
MEM_SAFE_DELETE(support_key);
|
||||
MEM_SAFE_DELETE(gpu_name);
|
||||
devices.clear_and_shrink();
|
||||
device_uuid.reinitialize(0);
|
||||
device_luid.reinitialize(0);
|
||||
device_luid_node_mask = 0;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name C-API
|
||||
* \{ */
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
GPUSupportLevel GPU_platform_support_level()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return GPG.support_level;
|
||||
}
|
||||
|
||||
const char *GPU_platform_vendor()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return GPG.vendor;
|
||||
}
|
||||
|
||||
const char *GPU_platform_renderer()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return GPG.renderer;
|
||||
}
|
||||
|
||||
const char *GPU_platform_version()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return GPG.version;
|
||||
}
|
||||
|
||||
const char *GPU_platform_support_level_key()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return GPG.support_key;
|
||||
}
|
||||
|
||||
const char *GPU_platform_gpu_name()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return GPG.gpu_name;
|
||||
}
|
||||
|
||||
GPUArchitectureType GPU_platform_architecture()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return GPG.architecture_type;
|
||||
}
|
||||
|
||||
bool GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver)
|
||||
{
|
||||
return GPU_type_matches_ex(device, os, driver, GPU_BACKEND_ANY);
|
||||
}
|
||||
|
||||
bool GPU_type_matches_ex(GPUDeviceType device,
|
||||
GPUOSType os,
|
||||
GPUDriverType driver,
|
||||
GPUBackendType backend)
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver) &&
|
||||
(GPG.backend & backend);
|
||||
}
|
||||
|
||||
Span<GPUDevice> GPU_platform_devices_list()
|
||||
{
|
||||
return GPG.devices.as_span();
|
||||
}
|
||||
|
||||
Span<uint8_t> GPU_platform_uuid()
|
||||
{
|
||||
return GPG.device_uuid.as_span();
|
||||
}
|
||||
|
||||
Span<uint8_t> GPU_platform_luid()
|
||||
{
|
||||
return GPG.device_luid.as_span();
|
||||
}
|
||||
|
||||
uint32_t GPU_platform_luid_node_mask()
|
||||
{
|
||||
return GPG.device_luid_node_mask;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GPUPlatformGlobal {
|
||||
public:
|
||||
bool initialized = false;
|
||||
GPUDeviceType device;
|
||||
GPUOSType os;
|
||||
GPUDriverType driver;
|
||||
GPUSupportLevel support_level;
|
||||
char *vendor = nullptr;
|
||||
char *renderer = nullptr;
|
||||
char *version = nullptr;
|
||||
char *support_key = nullptr;
|
||||
char *gpu_name = nullptr;
|
||||
GPUBackendType backend = GPU_BACKEND_NONE;
|
||||
GPUArchitectureType architecture_type = GPU_ARCHITECTURE_IMR;
|
||||
Vector<GPUDevice> devices;
|
||||
|
||||
/* The UUID of the device. Can be an empty array, since it is not supported on all platforms. */
|
||||
Array<uint8_t, 16> device_uuid;
|
||||
/* The LUID of the device. Can be an empty array, since it is not supported on all platforms. */
|
||||
Array<uint8_t, 8> device_luid;
|
||||
/* A bit field with the nth bit active identifying the nth device with the same LUID. Only
|
||||
* matters if device_luid is defined. */
|
||||
uint32_t device_luid_node_mask;
|
||||
|
||||
void init(GPUDeviceType gpu_device,
|
||||
GPUOSType os_type,
|
||||
GPUDriverType driver_type,
|
||||
GPUSupportLevel gpu_support_level,
|
||||
GPUBackendType backend,
|
||||
const char *vendor_str,
|
||||
const char *renderer_str,
|
||||
const char *version_str,
|
||||
GPUArchitectureType arch_type);
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
extern GPUPlatformGlobal GPG;
|
||||
|
||||
} // namespace blender::gpu
|
||||
18
blender-5.2.0/source/blender/gpu/intern/gpu_private.hh
Normal file
18
blender-5.2.0/source/blender/gpu/intern/gpu_private.hh
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* gpu_backend.cc */
|
||||
|
||||
void gpu_backend_init_resources();
|
||||
void gpu_backend_delete_resources();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class ProfileReport {
|
||||
private:
|
||||
std::fstream _report;
|
||||
Mutex _mutex;
|
||||
Map<uint64_t, int> _thread_ids;
|
||||
|
||||
ProfileReport()
|
||||
{
|
||||
_report.open("profile.json", std::ios::out);
|
||||
_report << R"([{"name":"process_name","ph":"M","pid":1,"args":{"name":"GPU"}})"
|
||||
",\n";
|
||||
_report << R"({"name":"process_name","ph":"M","pid":2,"args":{"name":"CPU"}})";
|
||||
}
|
||||
|
||||
~ProfileReport()
|
||||
{
|
||||
_report << "\n]\n";
|
||||
_report.close();
|
||||
}
|
||||
|
||||
public:
|
||||
static ProfileReport &get()
|
||||
{
|
||||
static ProfileReport singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
void add_group(StringRefNull name,
|
||||
uint64_t gpu_start,
|
||||
uint64_t gpu_end,
|
||||
uint64_t cpu_start,
|
||||
uint64_t cpu_end)
|
||||
{
|
||||
std::scoped_lock lock(_mutex);
|
||||
|
||||
size_t thread_hash = std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
int thread_id = _thread_ids.lookup_or_add(thread_hash, _thread_ids.size());
|
||||
|
||||
_report << fmt::format(
|
||||
",\n"
|
||||
R"({{"name":"{}","ph":"X","ts":{},"dur":{},"pid":1,"tid":{}}})",
|
||||
name.c_str(),
|
||||
gpu_start / uint64_t(1000),
|
||||
(gpu_end - gpu_start) / uint64_t(1000),
|
||||
thread_id);
|
||||
|
||||
_report << fmt::format(
|
||||
",\n"
|
||||
R"({{"name":"{}","ph":"X","ts":{},"dur":{},"pid":2,"tid":{}}})",
|
||||
name.c_str(),
|
||||
cpu_start / uint64_t(1000),
|
||||
(cpu_end - cpu_start) / uint64_t(1000),
|
||||
thread_id);
|
||||
}
|
||||
|
||||
void add_group_cpu(StringRefNull name, uint64_t cpu_start, uint64_t cpu_end)
|
||||
{
|
||||
std::scoped_lock lock(_mutex);
|
||||
|
||||
size_t thread_hash = std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
int thread_id = _thread_ids.lookup_or_add(thread_hash, _thread_ids.size());
|
||||
|
||||
_report << fmt::format(
|
||||
",\n"
|
||||
R"({{"name":"{}","ph":"X","ts":{},"dur":{},"pid":2,"tid":{}}})",
|
||||
name.c_str(),
|
||||
cpu_start / uint64_t(1000),
|
||||
(cpu_end - cpu_start) / uint64_t(1000),
|
||||
thread_id);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
17
blender-5.2.0/source/blender/gpu/intern/gpu_query.cc
Normal file
17
blender-5.2.0/source/blender/gpu/intern/gpu_query.cc
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "gpu_query.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* TODO(fclem): Make the associated C-API to use inside DRW profiler. */
|
||||
|
||||
} // namespace blender
|
||||
46
blender-5.2.0/source/blender/gpu/intern/gpu_query.hh
Normal file
46
blender-5.2.0/source/blender/gpu/intern/gpu_query.hh
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
#define QUERY_MIN_LEN 16
|
||||
|
||||
enum GPUQueryType {
|
||||
GPU_QUERY_OCCLUSION = 0,
|
||||
};
|
||||
|
||||
class QueryPool {
|
||||
public:
|
||||
virtual ~QueryPool() = default;
|
||||
|
||||
/**
|
||||
* Will start and end the query at this index inside the pool. The pool will resize
|
||||
* automatically but does not support sparse allocation. So prefer using consecutive indices.
|
||||
*/
|
||||
virtual void init(GPUQueryType type) = 0;
|
||||
|
||||
/**
|
||||
* Will start and end the query at this index inside the pool.
|
||||
* The pool will resize automatically.
|
||||
*/
|
||||
virtual void begin_query() = 0;
|
||||
virtual void end_query() = 0;
|
||||
|
||||
/**
|
||||
* Must be fed with a buffer large enough to contain all the queries issued.
|
||||
* IMPORTANT: Result for each query can be either binary or represent the number of samples
|
||||
* drawn.
|
||||
*/
|
||||
virtual void get_occlusion_result(MutableSpan<uint32_t> r_values) = 0;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
345
blender-5.2.0/source/blender/gpu/intern/gpu_select.cc
Normal file
345
blender-5.2.0/source/blender/gpu/intern/gpu_select.cc
Normal file
@@ -0,0 +1,345 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Interface for accessing GPU-related methods for selection. The semantics are
|
||||
* similar to `glRenderMode(GL_SELECT)` from older OpenGL versions.
|
||||
*/
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "GPU_select.hh"
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "gpu_select_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Types
|
||||
* \{ */
|
||||
|
||||
/* Internal algorithm used */
|
||||
enum GPUSelectAlgo {
|
||||
/**
|
||||
* `glBegin/EndQuery(GL_SAMPLES_PASSED... )`, `gpu_select_query.c`
|
||||
* Only sets 4th component (ID) correctly.
|
||||
*/
|
||||
ALGO_SAMPLE_QUERY = 1,
|
||||
/**
|
||||
* Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.cc`
|
||||
* Only sets 4th component (ID) correctly.
|
||||
*/
|
||||
ALGO_DEPTH_PICK = 2,
|
||||
/** Use Select-Next draw engine. */
|
||||
ALGO_SELECT_NEXT = 3,
|
||||
};
|
||||
|
||||
struct GPUSelectState {
|
||||
/* To ignore selection id calls when not initialized */
|
||||
bool select_is_active;
|
||||
/* mode of operation */
|
||||
GPUSelectMode mode;
|
||||
/* internal algorithm for selection */
|
||||
GPUSelectAlgo algorithm;
|
||||
/* allow GPU_select_begin/end without drawing */
|
||||
bool use_cache;
|
||||
/**
|
||||
* Signifies that #GPU_select_cache_begin has been called,
|
||||
* future calls to #GPU_select_begin should initialize the cache.
|
||||
*
|
||||
* \note #GPU_select_cache_begin could perform initialization but doesn't as it's inconvenient
|
||||
* for callers making the cache begin/end calls outside lower level selection logic
|
||||
* where the `mode` to pass to #GPU_select_begin yet isn't known.
|
||||
*/
|
||||
bool use_cache_needs_init;
|
||||
};
|
||||
|
||||
static GPUSelectState g_select_state = {false};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
static void gpu_select_begin_ex(GPUSelectBuffer *buffer,
|
||||
const rcti *input,
|
||||
const int radius,
|
||||
GPUSelectMode mode,
|
||||
int oldhits,
|
||||
bool use_select_next)
|
||||
{
|
||||
if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
|
||||
/* In the case hits was '-1',
|
||||
* don't start the second pass since it's not going to give useful results.
|
||||
* As well as buffer overflow in 'gpu_select_query_load_id'. */
|
||||
BLI_assert(oldhits != -1);
|
||||
}
|
||||
|
||||
g_select_state.select_is_active = true;
|
||||
g_select_state.mode = mode;
|
||||
|
||||
if (use_select_next) {
|
||||
g_select_state.algorithm = ALGO_SELECT_NEXT;
|
||||
}
|
||||
else if (ELEM(g_select_state.mode, GPU_SELECT_PICK_ALL, GPU_SELECT_PICK_NEAREST)) {
|
||||
g_select_state.algorithm = ALGO_DEPTH_PICK;
|
||||
}
|
||||
else {
|
||||
g_select_state.algorithm = ALGO_SAMPLE_QUERY;
|
||||
}
|
||||
|
||||
/* This function is called when cache has already been initialized,
|
||||
* so only manipulate cache values when cache is pending. */
|
||||
if (g_select_state.use_cache_needs_init) {
|
||||
g_select_state.use_cache_needs_init = false;
|
||||
|
||||
switch (g_select_state.algorithm) {
|
||||
case ALGO_SELECT_NEXT:
|
||||
case ALGO_SAMPLE_QUERY: {
|
||||
g_select_state.use_cache = false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
g_select_state.use_cache = true;
|
||||
gpu_select_pick_cache_begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (g_select_state.algorithm) {
|
||||
case ALGO_SELECT_NEXT: {
|
||||
gpu_select_next_begin(buffer, input, radius, mode);
|
||||
break;
|
||||
}
|
||||
case ALGO_SAMPLE_QUERY: {
|
||||
gpu_select_query_begin(buffer, input, mode, oldhits);
|
||||
break;
|
||||
}
|
||||
default: /* ALGO_DEPTH_PICK */
|
||||
{
|
||||
gpu_select_pick_begin(buffer, input, mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_select_begin_next(
|
||||
GPUSelectBuffer *buffer, const rcti *input, const int radius, GPUSelectMode mode, int oldhits)
|
||||
{
|
||||
gpu_select_begin_ex(buffer, input, radius, mode, oldhits, true);
|
||||
}
|
||||
|
||||
void GPU_select_begin(GPUSelectBuffer *buffer, const rcti *input, GPUSelectMode mode, int oldhits)
|
||||
{
|
||||
gpu_select_begin_ex(buffer, input, 0, mode, oldhits, false);
|
||||
}
|
||||
|
||||
bool GPU_select_load_id(uint id)
|
||||
{
|
||||
/* if no selection mode active, ignore */
|
||||
if (!g_select_state.select_is_active) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (g_select_state.algorithm) {
|
||||
case ALGO_SELECT_NEXT:
|
||||
/* This shouldn't use this pipeline. */
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
|
||||
case ALGO_SAMPLE_QUERY: {
|
||||
return gpu_select_query_load_id(id);
|
||||
}
|
||||
default: /* ALGO_DEPTH_PICK */
|
||||
{
|
||||
return gpu_select_pick_load_id(id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint GPU_select_end()
|
||||
{
|
||||
uint hits = 0;
|
||||
|
||||
switch (g_select_state.algorithm) {
|
||||
case ALGO_SELECT_NEXT: {
|
||||
hits = gpu_select_next_end();
|
||||
break;
|
||||
}
|
||||
case ALGO_SAMPLE_QUERY: {
|
||||
hits = gpu_select_query_end();
|
||||
break;
|
||||
}
|
||||
default: /* ALGO_DEPTH_PICK */
|
||||
{
|
||||
hits = gpu_select_pick_end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_select_state.select_is_active = false;
|
||||
|
||||
return hits;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Caching
|
||||
*
|
||||
* Support multiple begin/end's as long as they are within the initial region.
|
||||
* Currently only used by #ALGO_DEPTH_PICK.
|
||||
* \{ */
|
||||
|
||||
void GPU_select_cache_begin()
|
||||
{
|
||||
BLI_assert(g_select_state.select_is_active == false);
|
||||
/* Ensure #GPU_select_cache_end is always called. */
|
||||
BLI_assert(g_select_state.use_cache_needs_init == false);
|
||||
|
||||
/* Signal that cache should be used, instead of calling the algorithms cache-begin function.
|
||||
* This is more convenient as the exact method of selection may not be known by the caller. */
|
||||
g_select_state.use_cache_needs_init = true;
|
||||
}
|
||||
|
||||
void GPU_select_cache_load_id()
|
||||
{
|
||||
BLI_assert(g_select_state.use_cache == true);
|
||||
if (g_select_state.algorithm == ALGO_DEPTH_PICK) {
|
||||
gpu_select_pick_cache_load_id();
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_select_cache_end()
|
||||
{
|
||||
if (g_select_state.algorithm == ALGO_DEPTH_PICK) {
|
||||
BLI_assert(g_select_state.use_cache == true);
|
||||
gpu_select_pick_cache_end();
|
||||
}
|
||||
g_select_state.use_cache = false;
|
||||
/* Paranoid assignment, should already be false. */
|
||||
g_select_state.use_cache_needs_init = false;
|
||||
}
|
||||
|
||||
bool GPU_select_is_cached()
|
||||
{
|
||||
return g_select_state.use_cache && gpu_select_pick_is_cached();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p)
|
||||
{
|
||||
GPUSelectResult *a = static_cast<GPUSelectResult *>(const_cast<void *>(sel_a_p));
|
||||
GPUSelectResult *b = static_cast<GPUSelectResult *>(const_cast<void *>(sel_b_p));
|
||||
|
||||
if (a->depth < b->depth) {
|
||||
return -1;
|
||||
}
|
||||
if (a->depth > b->depth) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Depths match, sort by id. */
|
||||
/* NOTE: this is endianness-sensitive.
|
||||
* GPUSelectResult values are always expected to be little-endian. */
|
||||
uint sel_a = a->id;
|
||||
uint sel_b = b->id;
|
||||
|
||||
if (sel_a < sel_b) {
|
||||
return -1;
|
||||
}
|
||||
if (sel_a > sel_b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const GPUSelectResult *GPU_select_buffer_near(const Span<GPUSelectResult> hit_results)
|
||||
{
|
||||
const GPUSelectResult *hit_result_near = nullptr;
|
||||
uint depth_min = uint(-1);
|
||||
for (const GPUSelectResult &hit_result : hit_results) {
|
||||
if (hit_result.depth < depth_min) {
|
||||
BLI_assert(hit_result.id != -1);
|
||||
depth_min = hit_result.depth;
|
||||
hit_result_near = &hit_result;
|
||||
}
|
||||
}
|
||||
return hit_result_near;
|
||||
}
|
||||
|
||||
uint GPU_select_buffer_remove_by_id(MutableSpan<GPUSelectResult> hit_results, uint select_id)
|
||||
{
|
||||
uint index_src = 0;
|
||||
uint index_dst = 0;
|
||||
uint hits_final = 0;
|
||||
for (const GPUSelectResult &hit_result : hit_results) {
|
||||
if (hit_result.id != select_id) {
|
||||
if (index_dst != index_src) {
|
||||
hit_results[index_dst] = hit_result;
|
||||
}
|
||||
index_dst++;
|
||||
hits_final++;
|
||||
}
|
||||
index_src++;
|
||||
}
|
||||
return hits_final;
|
||||
}
|
||||
|
||||
void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_buf)
|
||||
{
|
||||
const int x = dst->xmin - src->xmin;
|
||||
const int y = dst->ymin - src->ymin;
|
||||
|
||||
BLI_assert(src->xmin <= dst->xmin && src->ymin <= dst->ymin && src->xmax >= dst->xmax &&
|
||||
src->ymax >= dst->ymax);
|
||||
BLI_assert(x >= 0 && y >= 0);
|
||||
|
||||
const int src_x = BLI_rcti_size_x(src);
|
||||
const int src_y = BLI_rcti_size_y(src);
|
||||
const int dst_x = BLI_rcti_size_x(dst);
|
||||
const int dst_y = BLI_rcti_size_y(dst);
|
||||
|
||||
BLI_assert(dst_x > 0 && dst_y > 0);
|
||||
|
||||
int last_px_id = src_x * (y + dst_y - 1) + (x + dst_x - 1);
|
||||
memset(&r_buf[last_px_id + 1], 0, (src_x * src_y - (last_px_id + 1)) * sizeof(*r_buf));
|
||||
|
||||
if (last_px_id < 0) {
|
||||
/* Nothing to write. */
|
||||
BLI_assert(last_px_id == -1);
|
||||
return;
|
||||
}
|
||||
|
||||
int last_px_written = dst_x * dst_y - 1;
|
||||
const int skip = src_x - dst_x;
|
||||
|
||||
while (true) {
|
||||
for (int i = dst_x; i--;) {
|
||||
r_buf[last_px_id--] = r_buf[last_px_written--];
|
||||
}
|
||||
if (last_px_written < 0) {
|
||||
break;
|
||||
}
|
||||
last_px_id -= skip;
|
||||
memset(&r_buf[last_px_id + 1], 0, skip * sizeof(*r_buf));
|
||||
}
|
||||
memset(r_buf, 0, (last_px_id + 1) * sizeof(*r_buf));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
95
blender-5.2.0/source/blender/gpu/intern/gpu_select_next.cc
Normal file
95
blender-5.2.0/source/blender/gpu/intern/gpu_select_next.cc
Normal file
@@ -0,0 +1,95 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Glue to make the new Select-Next engine work with the old GPU select API.
|
||||
*/
|
||||
#include <cfloat>
|
||||
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "gpu_select_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUSelectNextState {
|
||||
/** Result buffer set on initialization. */
|
||||
GPUSelectBuffer *buffer = nullptr;
|
||||
/** Area of the viewport to render / select from. */
|
||||
rcti rect = {0, 0, 0, 0};
|
||||
/** The radius relative to the center of `rect` in which to select. Ignored if 0. */
|
||||
int radius = 0;
|
||||
/** Number of hits. Set to -1 if it overflows buffer_len. */
|
||||
uint hits = -1;
|
||||
/** Mode of operation. */
|
||||
GPUSelectMode mode = GPUSelectMode::GPU_SELECT_INVALID;
|
||||
};
|
||||
|
||||
static GPUSelectNextState g_state = {};
|
||||
|
||||
void gpu_select_next_begin(GPUSelectBuffer *buffer,
|
||||
const rcti *input,
|
||||
const int radius,
|
||||
GPUSelectMode mode)
|
||||
|
||||
{
|
||||
g_state.buffer = buffer;
|
||||
g_state.rect = *input;
|
||||
g_state.mode = mode;
|
||||
g_state.radius = radius;
|
||||
}
|
||||
|
||||
int2 gpu_select_next_get_pick_area_center()
|
||||
{
|
||||
return int2(BLI_rcti_size_x(&g_state.rect) / 2, BLI_rcti_size_y(&g_state.rect) / 2);
|
||||
}
|
||||
|
||||
GPUSelectMode gpu_select_next_get_mode()
|
||||
{
|
||||
return g_state.mode;
|
||||
}
|
||||
|
||||
int gpu_select_next_get_radius()
|
||||
{
|
||||
return g_state.radius;
|
||||
}
|
||||
|
||||
void gpu_select_next_set_result(GPUSelectResult *hit_buf, uint hit_len)
|
||||
{
|
||||
const int old_size = g_state.buffer->storage.size();
|
||||
g_state.buffer->storage.resize(old_size + hit_len);
|
||||
MutableSpan<GPUSelectResult> hit_results = g_state.buffer->storage.as_mutable_span().slice(
|
||||
old_size, hit_len);
|
||||
const Span<GPUSelectResult> hits(hit_buf, hit_len);
|
||||
|
||||
/* TODO(fclem): There might be some conversion to do to align to the other APIs output. */
|
||||
switch (g_state.mode) {
|
||||
case GPUSelectMode::GPU_SELECT_ALL:
|
||||
hit_results.copy_from(hits);
|
||||
break;
|
||||
case GPUSelectMode::GPU_SELECT_PICK_ALL:
|
||||
hit_results.copy_from(hits);
|
||||
break;
|
||||
case GPUSelectMode::GPU_SELECT_PICK_NEAREST:
|
||||
hit_results.copy_from(hits);
|
||||
break;
|
||||
case GPUSelectMode::GPU_SELECT_NEAREST_FIRST_PASS:
|
||||
case GPUSelectMode::GPU_SELECT_NEAREST_SECOND_PASS:
|
||||
case GPUSelectMode::GPU_SELECT_INVALID:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
g_state.hits = hit_len;
|
||||
}
|
||||
|
||||
uint gpu_select_next_end()
|
||||
{
|
||||
return g_state.hits;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
755
blender-5.2.0/source/blender/gpu/intern/gpu_select_pick.cc
Normal file
755
blender-5.2.0/source/blender/gpu/intern/gpu_select_pick.cc
Normal file
@@ -0,0 +1,755 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Custom select code for picking small regions (not efficient for large regions).
|
||||
* `gpu_select_pick_*` API.
|
||||
*/
|
||||
#include <cfloat>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_select.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "gpu_select_private.hh"
|
||||
|
||||
#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
// #define DEBUG_PRINT
|
||||
|
||||
/* Alloc number for depths */
|
||||
#define ALLOC_DEPTHS 200
|
||||
|
||||
/* Z-depth of cleared depth buffer */
|
||||
#define DEPTH_MAX 0xffffffff
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #SubRectStride
|
||||
* \{ */
|
||||
|
||||
/** For looping over a sub-region of a #rcti, could be moved into `rct.c`. */
|
||||
struct SubRectStride {
|
||||
/** Start here. */
|
||||
uint start;
|
||||
/** Read these. */
|
||||
uint span;
|
||||
/** `len` times (read span 'len' times). */
|
||||
uint span_len;
|
||||
/** Skip those. */
|
||||
uint skip;
|
||||
};
|
||||
|
||||
/** We may want to change back to float if `uint` isn't well supported. */
|
||||
using depth_t = uint;
|
||||
|
||||
/**
|
||||
* Calculate values needed for looping over a sub-region (smaller buffer within a larger buffer).
|
||||
*
|
||||
* 'src' must be bigger than 'dst'.
|
||||
*/
|
||||
static void rect_subregion_stride_calc(const rcti *src, const rcti *dst, SubRectStride *r_sub)
|
||||
{
|
||||
const int src_x = BLI_rcti_size_x(src);
|
||||
// const int src_y = BLI_rcti_size_y(src);
|
||||
const int dst_x = BLI_rcti_size_x(dst);
|
||||
const int dst_y = BLI_rcti_size_y(dst);
|
||||
const int x = dst->xmin - src->xmin;
|
||||
const int y = dst->ymin - src->ymin;
|
||||
|
||||
BLI_assert(src->xmin <= dst->xmin && src->ymin <= dst->ymin && src->xmax >= dst->xmax &&
|
||||
src->ymax >= dst->ymax);
|
||||
BLI_assert(x >= 0 && y >= 0);
|
||||
|
||||
r_sub->start = uint((src_x * y) + x);
|
||||
r_sub->span = uint(dst_x);
|
||||
r_sub->span_len = uint(dst_y);
|
||||
r_sub->skip = uint(src_x - dst_x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore depth clearing as a change,
|
||||
* only check if its been changed _and_ filled in (ignore clearing since XRAY does this).
|
||||
*/
|
||||
BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr)
|
||||
{
|
||||
return (*prev != *curr) && (*curr != DEPTH_MAX);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #DepthBufCache
|
||||
*
|
||||
* Result of reading #GPU_framebuffer_read_depth,
|
||||
* use for both cache and non-cached storage.
|
||||
* \{ */
|
||||
|
||||
/** Store result of #GPU_framebuffer_read_depth. */
|
||||
struct DepthBufCache {
|
||||
DepthBufCache *next, *prev;
|
||||
uint id;
|
||||
depth_t buf[0];
|
||||
};
|
||||
|
||||
static DepthBufCache *depth_buf_malloc(uint rect_len)
|
||||
{
|
||||
DepthBufCache *rect = static_cast<DepthBufCache *>(
|
||||
MEM_new_uninitialized(sizeof(DepthBufCache) + sizeof(depth_t) * rect_len, __func__));
|
||||
rect->id = SELECT_ID_NONE;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static bool depth_buf_rect_depth_any(const DepthBufCache *rect_depth, uint rect_len)
|
||||
{
|
||||
const depth_t *curr = rect_depth->buf;
|
||||
for (uint i = 0; i < rect_len; i++, curr++) {
|
||||
if (*curr != DEPTH_MAX) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool depth_buf_subrect_depth_any(const DepthBufCache *rect_depth,
|
||||
const SubRectStride *sub_rect)
|
||||
{
|
||||
const depth_t *curr = rect_depth->buf + sub_rect->start;
|
||||
for (uint i = 0; i < sub_rect->span_len; i++) {
|
||||
const depth_t *curr_end = curr + sub_rect->span;
|
||||
for (; curr < curr_end; curr++, curr++) {
|
||||
if (*curr != DEPTH_MAX) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
curr += sub_rect->skip;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool depth_buf_rect_depth_any_filled(const DepthBufCache *rect_prev,
|
||||
const DepthBufCache *rect_curr,
|
||||
uint rect_len)
|
||||
{
|
||||
#if 0
|
||||
return memcmp(rect_depth_a->buf, rect_depth_b->buf, rect_len * sizeof(depth_t)) != 0;
|
||||
#else
|
||||
const depth_t *prev = rect_prev->buf;
|
||||
const depth_t *curr = rect_curr->buf;
|
||||
for (uint i = 0; i < rect_len; i++, curr++, prev++) {
|
||||
if (depth_is_filled(prev, curr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Both buffers are the same size, just check if the sub-rect contains any differences.
|
||||
*/
|
||||
static bool depth_buf_subrect_depth_any_filled(const DepthBufCache *rect_src,
|
||||
const DepthBufCache *rect_dst,
|
||||
const SubRectStride *sub_rect)
|
||||
{
|
||||
/* Same as #depth_buf_rect_depth_any_filled but different rectangle sizes. */
|
||||
const depth_t *prev = rect_src->buf + sub_rect->start;
|
||||
const depth_t *curr = rect_dst->buf + sub_rect->start;
|
||||
for (uint i = 0; i < sub_rect->span_len; i++) {
|
||||
const depth_t *curr_end = curr + sub_rect->span;
|
||||
for (; curr < curr_end; prev++, curr++) {
|
||||
if (depth_is_filled(prev, curr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
prev += sub_rect->skip;
|
||||
curr += sub_rect->skip;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #DepthID
|
||||
*
|
||||
* Internal structure for storing hits.
|
||||
* \{ */
|
||||
|
||||
struct DepthID {
|
||||
uint id;
|
||||
depth_t depth;
|
||||
};
|
||||
|
||||
static int depth_id_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
const DepthID *d1 = static_cast<const DepthID *>(v1), *d2 = static_cast<const DepthID *>(v2);
|
||||
if (d1->id < d2->id) {
|
||||
return -1;
|
||||
}
|
||||
if (d1->id > d2->id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int depth_cmp(const void *v1, const void *v2)
|
||||
{
|
||||
const DepthID *d1 = static_cast<const DepthID *>(v1), *d2 = static_cast<const DepthID *>(v2);
|
||||
if (d1->depth < d2->depth) {
|
||||
return -1;
|
||||
}
|
||||
if (d1->depth > d2->depth) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Main Selection Begin/End/Load API
|
||||
* \{ */
|
||||
|
||||
/** Depth sorting. */
|
||||
struct GPUPickState {
|
||||
/** Cache on initialization. */
|
||||
GPUSelectBuffer *buffer;
|
||||
/** Mode of this operation. */
|
||||
GPUSelectMode mode;
|
||||
|
||||
/** GPU drawing, never use when `is_cached == true`. */
|
||||
struct {
|
||||
/** The current depth, accumulated while drawing. */
|
||||
DepthBufCache *rect_depth;
|
||||
/** Scratch buffer, avoid allocations every time (when not caching). */
|
||||
DepthBufCache *rect_depth_test;
|
||||
|
||||
/** Pass to `GPU_framebuffer_read_depth(x, y, w, h)`. */
|
||||
int clip_readpixels[4];
|
||||
|
||||
/** Set after first draw. */
|
||||
bool is_init;
|
||||
uint prev_id;
|
||||
} gpu;
|
||||
|
||||
/**
|
||||
* `src`: data stored in 'cache' and 'gpu',
|
||||
* `dst`: use when cached region is smaller (where `src` -> `dst` isn't 1:1).
|
||||
*/
|
||||
struct {
|
||||
rcti clip_rect;
|
||||
uint rect_len;
|
||||
} src, dst;
|
||||
|
||||
/** Store cache between `GPU_select_cache_begin/end` */
|
||||
bool use_cache;
|
||||
bool is_cached;
|
||||
struct {
|
||||
/**
|
||||
* Cleanup used for iterating over both source and destination buffers:
|
||||
* `src.clip_rect` -> `dst.clip_rect`.
|
||||
*/
|
||||
SubRectStride sub_rect;
|
||||
|
||||
/** List of #DepthBufCache, sized of 'src.clip_rect'. */
|
||||
ListBaseT<DepthBufCache> bufs;
|
||||
} cache;
|
||||
|
||||
/** Picking methods. */
|
||||
union {
|
||||
/** #GPU_SELECT_PICK_ALL */
|
||||
struct {
|
||||
DepthID *hits;
|
||||
uint hits_len;
|
||||
uint hits_len_alloc;
|
||||
} all;
|
||||
|
||||
/** #GPU_SELECT_PICK_NEAREST */
|
||||
struct {
|
||||
uint *rect_id;
|
||||
} nearest;
|
||||
};
|
||||
|
||||
/** Previous state to restore after drawing. */
|
||||
int viewport[4];
|
||||
int scissor[4];
|
||||
GPUWriteMask write_mask;
|
||||
GPUDepthTest depth_test;
|
||||
};
|
||||
|
||||
static GPUPickState g_pick_state{};
|
||||
|
||||
void gpu_select_pick_begin(GPUSelectBuffer *buffer, const rcti *input, GPUSelectMode mode)
|
||||
{
|
||||
GPUPickState *ps = &g_pick_state;
|
||||
|
||||
#ifdef DEBUG_PRINT
|
||||
printf("%s: mode=%d, use_cache=%d, is_cache=%d\n",
|
||||
__func__,
|
||||
int(mode),
|
||||
ps->use_cache,
|
||||
ps->is_cached);
|
||||
#endif
|
||||
|
||||
GPU_debug_group_begin("Selection Pick");
|
||||
|
||||
ps->buffer = buffer;
|
||||
ps->mode = mode;
|
||||
|
||||
const uint rect_len = uint(BLI_rcti_size_x(input) * BLI_rcti_size_y(input));
|
||||
ps->dst.clip_rect = *input;
|
||||
ps->dst.rect_len = rect_len;
|
||||
|
||||
/* Avoids unnecessary GPU operations when cache is available and they are unnecessary. */
|
||||
if (ps->is_cached == false) {
|
||||
ps->write_mask = GPU_write_mask_get();
|
||||
ps->depth_test = GPU_depth_test_get();
|
||||
GPU_scissor_get(ps->scissor);
|
||||
|
||||
/* Disable writing to the frame-buffer. */
|
||||
GPU_color_mask(false, false, false, false);
|
||||
|
||||
GPU_depth_mask(true);
|
||||
/* Always use #GPU_DEPTH_LESS_EQUAL even though #GPU_SELECT_PICK_ALL always clears the buffer.
|
||||
* This is because individual objects themselves might have sections that overlap and we need
|
||||
* these to have the correct distance information. */
|
||||
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
||||
|
||||
float viewport[4];
|
||||
GPU_viewport_size_get_f(viewport);
|
||||
|
||||
ps->src.clip_rect = *input;
|
||||
ps->src.rect_len = rect_len;
|
||||
|
||||
ps->gpu.clip_readpixels[0] = int(viewport[0]);
|
||||
ps->gpu.clip_readpixels[1] = int(viewport[1]);
|
||||
ps->gpu.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect);
|
||||
ps->gpu.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect);
|
||||
|
||||
GPU_viewport(UNPACK4(ps->gpu.clip_readpixels));
|
||||
|
||||
/* It's possible we don't want to clear depth buffer,
|
||||
* so existing elements are masked by current z-buffer. */
|
||||
GPU_clear_depth(1.0f);
|
||||
|
||||
/* scratch buffer (read new values here) */
|
||||
ps->gpu.rect_depth_test = depth_buf_malloc(rect_len);
|
||||
ps->gpu.rect_depth = depth_buf_malloc(rect_len);
|
||||
|
||||
/* Set initial 'far' value. */
|
||||
for (uint i = 0; i < rect_len; i++) {
|
||||
ps->gpu.rect_depth->buf[i] = DEPTH_MAX;
|
||||
}
|
||||
|
||||
ps->gpu.is_init = false;
|
||||
ps->gpu.prev_id = 0;
|
||||
}
|
||||
else {
|
||||
/* Using cache `ps->is_cached == true`. */
|
||||
/* `src.clip_rect` -> `dst.clip_rect`. */
|
||||
rect_subregion_stride_calc(&ps->src.clip_rect, &ps->dst.clip_rect, &ps->cache.sub_rect);
|
||||
BLI_assert(ps->gpu.rect_depth == nullptr);
|
||||
BLI_assert(ps->gpu.rect_depth_test == nullptr);
|
||||
}
|
||||
|
||||
if (mode == GPU_SELECT_PICK_ALL) {
|
||||
ps->all.hits = MEM_new_array_uninitialized<DepthID>(ALLOC_DEPTHS, __func__);
|
||||
ps->all.hits_len = 0;
|
||||
ps->all.hits_len_alloc = ALLOC_DEPTHS;
|
||||
}
|
||||
else {
|
||||
/* Set to 0xff for #SELECT_ID_NONE. */
|
||||
ps->nearest.rect_id = MEM_new_array_uninitialized<uint>(ps->dst.rect_len, __func__);
|
||||
memset(ps->nearest.rect_id, 0xff, sizeof(uint) * ps->dst.rect_len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given 2x depths, we know are different - update the depth information
|
||||
* use for both cached/uncached depth buffers.
|
||||
*/
|
||||
static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr)
|
||||
{
|
||||
GPUPickState *ps = &g_pick_state;
|
||||
const uint id = rect_curr->id;
|
||||
/* find the best depth for this pass and store in 'all.hits' */
|
||||
depth_t depth_best = DEPTH_MAX;
|
||||
|
||||
#define EVAL_TEST() \
|
||||
if (depth_best > *curr) { \
|
||||
depth_best = *curr; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
if (ps->is_cached == false) {
|
||||
const depth_t *curr = rect_curr->buf;
|
||||
BLI_assert(ps->src.rect_len == ps->dst.rect_len);
|
||||
const uint rect_len = ps->src.rect_len;
|
||||
for (uint i = 0; i < rect_len; i++, curr++) {
|
||||
EVAL_TEST();
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Same as above but different rectangle sizes. */
|
||||
const depth_t *curr = rect_curr->buf + ps->cache.sub_rect.start;
|
||||
for (uint i = 0; i < ps->cache.sub_rect.span_len; i++) {
|
||||
const depth_t *curr_end = curr + ps->cache.sub_rect.span;
|
||||
for (; curr < curr_end; curr++) {
|
||||
EVAL_TEST();
|
||||
}
|
||||
curr += ps->cache.sub_rect.skip;
|
||||
}
|
||||
}
|
||||
|
||||
#undef EVAL_TEST
|
||||
|
||||
/* Ensure enough space. */
|
||||
if (UNLIKELY(ps->all.hits_len == ps->all.hits_len_alloc)) {
|
||||
ps->all.hits_len_alloc += ALLOC_DEPTHS;
|
||||
ps->all.hits = static_cast<DepthID *>(
|
||||
MEM_realloc_uninitialized(ps->all.hits, ps->all.hits_len_alloc * sizeof(*ps->all.hits)));
|
||||
}
|
||||
DepthID *d = &ps->all.hits[ps->all.hits_len++];
|
||||
d->id = id;
|
||||
d->depth = depth_best;
|
||||
}
|
||||
|
||||
static void gpu_select_load_id_pass_nearest(const DepthBufCache *rect_prev,
|
||||
const DepthBufCache *rect_curr)
|
||||
{
|
||||
GPUPickState *ps = &g_pick_state;
|
||||
const uint id = rect_curr->id;
|
||||
/* Keep track each pixels ID in `nearest.rect_id`. */
|
||||
if (id != SELECT_ID_NONE) {
|
||||
uint *id_ptr = ps->nearest.rect_id;
|
||||
|
||||
/* Check against DEPTH_MAX because XRAY will clear the buffer,
|
||||
* so previously set values will become unset.
|
||||
* In this case just leave those id's left as-is. */
|
||||
#define EVAL_TEST() \
|
||||
if (depth_is_filled(prev, curr)) { \
|
||||
*id_ptr = id; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
if (ps->is_cached == false) {
|
||||
const depth_t *prev = rect_prev->buf;
|
||||
const depth_t *curr = rect_curr->buf;
|
||||
BLI_assert(ps->src.rect_len == ps->dst.rect_len);
|
||||
const uint rect_len = ps->src.rect_len;
|
||||
for (uint i = 0; i < rect_len; i++, curr++, prev++, id_ptr++) {
|
||||
EVAL_TEST();
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* same as above but different rect sizes */
|
||||
const depth_t *prev = rect_prev->buf + ps->cache.sub_rect.start;
|
||||
const depth_t *curr = rect_curr->buf + ps->cache.sub_rect.start;
|
||||
for (uint i = 0; i < ps->cache.sub_rect.span_len; i++) {
|
||||
const depth_t *curr_end = curr + ps->cache.sub_rect.span;
|
||||
for (; curr < curr_end; prev++, curr++, id_ptr++) {
|
||||
EVAL_TEST();
|
||||
}
|
||||
prev += ps->cache.sub_rect.skip;
|
||||
curr += ps->cache.sub_rect.skip;
|
||||
}
|
||||
}
|
||||
|
||||
#undef EVAL_TEST
|
||||
}
|
||||
}
|
||||
|
||||
bool gpu_select_pick_load_id(uint id, bool end)
|
||||
{
|
||||
GPUPickState *ps = &g_pick_state;
|
||||
|
||||
if (ps->gpu.is_init) {
|
||||
if (id == ps->gpu.prev_id && !end) {
|
||||
/* No need to read if we are still drawing for the same id since
|
||||
* all these depths will be merged / de-duplicated in the end. */
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint rect_len = ps->src.rect_len;
|
||||
gpu::FrameBuffer *fb = GPU_framebuffer_active_get();
|
||||
GPU_framebuffer_read_depth(
|
||||
fb, UNPACK4(ps->gpu.clip_readpixels), GPU_DATA_UINT, ps->gpu.rect_depth_test->buf);
|
||||
/* Perform initial check since most cases the array remains unchanged. */
|
||||
|
||||
bool do_pass = false;
|
||||
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
|
||||
if (depth_buf_rect_depth_any(ps->gpu.rect_depth_test, rect_len)) {
|
||||
ps->gpu.rect_depth_test->id = ps->gpu.prev_id;
|
||||
gpu_select_load_id_pass_all(ps->gpu.rect_depth_test);
|
||||
do_pass = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (depth_buf_rect_depth_any_filled(ps->gpu.rect_depth, ps->gpu.rect_depth_test, rect_len)) {
|
||||
ps->gpu.rect_depth_test->id = ps->gpu.prev_id;
|
||||
gpu_select_load_id_pass_nearest(ps->gpu.rect_depth, ps->gpu.rect_depth_test);
|
||||
do_pass = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_pass) {
|
||||
/* Store depth in cache */
|
||||
if (ps->use_cache) {
|
||||
BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth);
|
||||
ps->gpu.rect_depth = depth_buf_malloc(ps->src.rect_len);
|
||||
}
|
||||
|
||||
std::swap(ps->gpu.rect_depth, ps->gpu.rect_depth_test);
|
||||
|
||||
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
|
||||
/* (fclem) This is to be on the safe side. I don't know if this is required. */
|
||||
bool prev_depth_mask = GPU_depth_mask_get();
|
||||
/* we want new depths every time */
|
||||
GPU_depth_mask(true);
|
||||
GPU_clear_depth(1.0f);
|
||||
|
||||
GPU_depth_mask(prev_depth_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ps->gpu.is_init = true;
|
||||
ps->gpu.prev_id = id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint gpu_select_pick_end()
|
||||
{
|
||||
GPUPickState *ps = &g_pick_state;
|
||||
|
||||
#ifdef DEBUG_PRINT
|
||||
printf("%s\n", __func__);
|
||||
#endif
|
||||
|
||||
if (ps->is_cached == false) {
|
||||
if (ps->gpu.is_init) {
|
||||
/* force finishing last pass */
|
||||
gpu_select_pick_load_id(ps->gpu.prev_id, true);
|
||||
}
|
||||
GPU_write_mask(ps->write_mask);
|
||||
GPU_depth_test(ps->depth_test);
|
||||
GPU_viewport(UNPACK4(ps->viewport));
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
|
||||
/* Assign but never free directly since it may be in cache. */
|
||||
DepthBufCache *rect_depth_final;
|
||||
|
||||
/* Store depth in cache */
|
||||
if (ps->use_cache && !ps->is_cached) {
|
||||
BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth);
|
||||
ps->gpu.rect_depth = nullptr;
|
||||
rect_depth_final = static_cast<DepthBufCache *>(ps->cache.bufs.last);
|
||||
}
|
||||
else if (ps->is_cached) {
|
||||
rect_depth_final = static_cast<DepthBufCache *>(ps->cache.bufs.last);
|
||||
}
|
||||
else {
|
||||
/* Common case, no cache. */
|
||||
rect_depth_final = ps->gpu.rect_depth;
|
||||
}
|
||||
|
||||
DepthID *depth_data;
|
||||
uint depth_data_len = 0;
|
||||
|
||||
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
|
||||
depth_data = ps->all.hits;
|
||||
depth_data_len = ps->all.hits_len;
|
||||
/* Move ownership. */
|
||||
ps->all.hits = nullptr;
|
||||
ps->all.hits_len = 0;
|
||||
ps->all.hits_len_alloc = 0;
|
||||
}
|
||||
else {
|
||||
/* #GPU_SELECT_PICK_NEAREST */
|
||||
|
||||
/* Over allocate (unlikely we have as many depths as pixels). */
|
||||
uint depth_data_len_first_pass = 0;
|
||||
depth_data = MEM_new_array_uninitialized<DepthID>(ps->dst.rect_len, __func__);
|
||||
|
||||
/* Partially de-duplicating copy,
|
||||
* when contiguous ID's are found - update their closest depth.
|
||||
* This isn't essential but means there is less data to sort. */
|
||||
|
||||
#define EVAL_TEST(i_src, i_dst) \
|
||||
{ \
|
||||
const uint id = ps->nearest.rect_id[i_dst]; \
|
||||
if (id != SELECT_ID_NONE) { \
|
||||
const depth_t depth = rect_depth_final->buf[i_src]; \
|
||||
if (depth_last == nullptr || depth_last->id != id) { \
|
||||
DepthID *d = &depth_data[depth_data_len_first_pass++]; \
|
||||
d->id = id; \
|
||||
d->depth = depth; \
|
||||
} \
|
||||
else if (depth_last->depth > depth) { \
|
||||
depth_last->depth = depth; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
{
|
||||
DepthID *depth_last = nullptr;
|
||||
if (ps->is_cached == false) {
|
||||
for (uint i = 0; i < ps->src.rect_len; i++) {
|
||||
EVAL_TEST(i, i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Same as above but different rectangle sizes. */
|
||||
uint i_src = ps->cache.sub_rect.start, i_dst = 0;
|
||||
for (uint j = 0; j < ps->cache.sub_rect.span_len; j++) {
|
||||
const uint i_src_end = i_src + ps->cache.sub_rect.span;
|
||||
for (; i_src < i_src_end; i_src++, i_dst++) {
|
||||
EVAL_TEST(i_src, i_dst);
|
||||
}
|
||||
i_src += ps->cache.sub_rect.skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef EVAL_TEST
|
||||
|
||||
qsort(depth_data, depth_data_len_first_pass, sizeof(DepthID), depth_id_cmp);
|
||||
|
||||
/* Sort by ID's then keep the best depth for each ID. */
|
||||
depth_data_len = 0;
|
||||
{
|
||||
DepthID *depth_last = nullptr;
|
||||
for (uint i = 0; i < depth_data_len_first_pass; i++) {
|
||||
if (depth_last == nullptr || depth_last->id != depth_data[i].id) {
|
||||
depth_last = &depth_data[depth_data_len++];
|
||||
*depth_last = depth_data[i];
|
||||
}
|
||||
else if (depth_last->depth > depth_data[i].depth) {
|
||||
depth_last->depth = depth_data[i].depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally sort each unique (id, depth) pair by depth
|
||||
* so the final hit-list is sorted by depth (nearest first). */
|
||||
uint hits = 0;
|
||||
|
||||
/* Leave sorting up to the caller. */
|
||||
qsort(depth_data, depth_data_len, sizeof(DepthID), depth_cmp);
|
||||
|
||||
g_pick_state.buffer->storage.reserve(g_pick_state.buffer->storage.size() + depth_data_len);
|
||||
for (uint i = 0; i < depth_data_len; i++) {
|
||||
#ifdef DEBUG_PRINT
|
||||
printf(" hit: %u: depth %u\n", depth_data[i].id, depth_data[i].depth);
|
||||
#endif
|
||||
GPUSelectResult hit_result{};
|
||||
hit_result.id = depth_data[i].id;
|
||||
hit_result.depth = depth_data[i].depth;
|
||||
g_pick_state.buffer->storage.append_unchecked(hit_result);
|
||||
hits++;
|
||||
}
|
||||
|
||||
MEM_delete(depth_data);
|
||||
|
||||
MEM_SAFE_DELETE(ps->gpu.rect_depth);
|
||||
MEM_SAFE_DELETE(ps->gpu.rect_depth_test);
|
||||
|
||||
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
|
||||
/* 'hits' already freed as 'depth_data' */
|
||||
}
|
||||
else {
|
||||
MEM_delete(ps->nearest.rect_id);
|
||||
ps->nearest.rect_id = nullptr;
|
||||
}
|
||||
|
||||
if (ps->use_cache) {
|
||||
ps->is_cached = true;
|
||||
}
|
||||
|
||||
return hits;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Caching
|
||||
*
|
||||
* Support multiple begin/end's reusing depth buffers.
|
||||
* \{ */
|
||||
|
||||
void gpu_select_pick_cache_begin()
|
||||
{
|
||||
BLI_assert(g_pick_state.use_cache == false);
|
||||
#ifdef DEBUG_PRINT
|
||||
printf("%s\n", __func__);
|
||||
#endif
|
||||
g_pick_state.use_cache = true;
|
||||
g_pick_state.is_cached = false;
|
||||
}
|
||||
|
||||
void gpu_select_pick_cache_end()
|
||||
{
|
||||
#ifdef DEBUG_PRINT
|
||||
printf("%s: with %d buffers\n", __func__, g_pick_state.cache.bufs.count());
|
||||
#endif
|
||||
g_pick_state.use_cache = false;
|
||||
g_pick_state.is_cached = false;
|
||||
|
||||
g_pick_state.cache.bufs.free_no_destruct();
|
||||
}
|
||||
|
||||
bool gpu_select_pick_is_cached()
|
||||
{
|
||||
return g_pick_state.is_cached;
|
||||
}
|
||||
|
||||
void gpu_select_pick_cache_load_id()
|
||||
{
|
||||
BLI_assert(g_pick_state.is_cached == true);
|
||||
GPUPickState *ps = &g_pick_state;
|
||||
#ifdef DEBUG_PRINT
|
||||
printf("%s (building depth from cache)\n", __func__);
|
||||
#endif
|
||||
for (DepthBufCache &rect_depth : ps->cache.bufs) {
|
||||
if (rect_depth.next != nullptr) {
|
||||
/* We know the buffers differ, but this sub-region may not.
|
||||
* Double check before adding an id-pass. */
|
||||
if (g_pick_state.mode == GPU_SELECT_PICK_ALL) {
|
||||
if (depth_buf_subrect_depth_any(rect_depth.next, &ps->cache.sub_rect)) {
|
||||
gpu_select_load_id_pass_all(rect_depth.next);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (depth_buf_subrect_depth_any_filled(&rect_depth, rect_depth.next, &ps->cache.sub_rect))
|
||||
{
|
||||
gpu_select_load_id_pass_nearest(&rect_depth, rect_depth.next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Selection implementations.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "GPU_select.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* gpu_select_pick */
|
||||
|
||||
void gpu_select_pick_begin(GPUSelectBuffer *buffer, const rcti *input, GPUSelectMode mode);
|
||||
bool gpu_select_pick_load_id(uint id, bool end);
|
||||
uint gpu_select_pick_end();
|
||||
|
||||
void gpu_select_pick_cache_begin();
|
||||
void gpu_select_pick_cache_end();
|
||||
/**
|
||||
* \return true if drawing is not needed.
|
||||
*/
|
||||
bool gpu_select_pick_is_cached();
|
||||
void gpu_select_pick_cache_load_id();
|
||||
|
||||
/* gpu_select_sample_query */
|
||||
|
||||
void gpu_select_query_begin(GPUSelectBuffer *buffer,
|
||||
const rcti *input,
|
||||
GPUSelectMode mode,
|
||||
int oldhits);
|
||||
bool gpu_select_query_load_id(uint id);
|
||||
uint gpu_select_query_end();
|
||||
|
||||
/* gpu_select_next */
|
||||
|
||||
void gpu_select_next_begin(GPUSelectBuffer *buffer,
|
||||
const rcti *input,
|
||||
int radius,
|
||||
GPUSelectMode mode);
|
||||
uint gpu_select_next_end();
|
||||
|
||||
/**
|
||||
* Returns the center relative to the corner of the rect stored in the GPUSelectNextState.
|
||||
*/
|
||||
int2 gpu_select_next_get_pick_area_center();
|
||||
GPUSelectMode gpu_select_next_get_mode();
|
||||
int gpu_select_next_get_radius();
|
||||
void gpu_select_next_set_result(GPUSelectResult *hit_buf, uint hit_len);
|
||||
|
||||
#define SELECT_ID_NONE ((uint)0xffffffff)
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,183 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Interface for accessing GPU-related methods for selection. The semantics will be
|
||||
* similar to `glRenderMode(GL_SELECT)` since the goal is to maintain compatibility.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_select.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_query.hh"
|
||||
|
||||
#include "gpu_select_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
struct GPUSelectQueryState {
|
||||
/** Tracks whether a query has been issued so that gpu_load_id can end the previous one. */
|
||||
bool query_issued;
|
||||
/** GPU queries abstraction. Contains an array of queries. */
|
||||
QueryPool *queries;
|
||||
/** Array holding the id corresponding id to each query. */
|
||||
Vector<uint, QUERY_MIN_LEN> *ids;
|
||||
/** Cache on initialization. */
|
||||
GPUSelectBuffer *buffer;
|
||||
/** Mode of operation. */
|
||||
GPUSelectMode mode;
|
||||
uint index;
|
||||
int oldhits;
|
||||
|
||||
/** Previous state to restore after drawing. */
|
||||
int viewport[4];
|
||||
int scissor[4];
|
||||
GPUWriteMask write_mask;
|
||||
GPUDepthTest depth_test;
|
||||
};
|
||||
|
||||
static GPUSelectQueryState g_query_state = {false};
|
||||
|
||||
void gpu_select_query_begin(GPUSelectBuffer *buffer,
|
||||
const rcti *input,
|
||||
const GPUSelectMode mode,
|
||||
int oldhits)
|
||||
{
|
||||
GPU_debug_group_begin("Selection Queries");
|
||||
|
||||
g_query_state.query_issued = false;
|
||||
g_query_state.buffer = buffer;
|
||||
g_query_state.mode = mode;
|
||||
g_query_state.index = 0;
|
||||
g_query_state.oldhits = oldhits;
|
||||
|
||||
g_query_state.ids = new Vector<uint, QUERY_MIN_LEN>();
|
||||
g_query_state.queries = GPUBackend::get()->querypool_alloc();
|
||||
g_query_state.queries->init(GPU_QUERY_OCCLUSION);
|
||||
|
||||
g_query_state.write_mask = GPU_write_mask_get();
|
||||
g_query_state.depth_test = GPU_depth_test_get();
|
||||
GPU_scissor_get(g_query_state.scissor);
|
||||
GPU_viewport_size_get_i(g_query_state.viewport);
|
||||
|
||||
/* Write to color buffer. Seems to fix issues with selecting alpha blended geom (see #7997). */
|
||||
GPU_color_mask(true, true, true, true);
|
||||
|
||||
/* In order to save some fill rate we minimize the viewport using rect.
|
||||
* We need to get the region of the viewport so that our geometry doesn't
|
||||
* get rejected before the depth test. Should probably cull rect against
|
||||
* the viewport but this is a rare case I think */
|
||||
|
||||
const int viewport[4] = {
|
||||
UNPACK2(g_query_state.viewport), BLI_rcti_size_x(input), BLI_rcti_size_y(input)};
|
||||
|
||||
GPU_viewport(UNPACK4(viewport));
|
||||
GPU_scissor(UNPACK4(viewport));
|
||||
GPU_scissor_test(false);
|
||||
|
||||
/* occlusion queries operates on fragments that pass tests and since we are interested on all
|
||||
* objects in the view frustum independently of their order, we need to disable the depth test */
|
||||
if (mode == GPU_SELECT_ALL) {
|
||||
/* #glQueries on Windows+Intel drivers only works with depth testing turned on.
|
||||
* See #62947 for details */
|
||||
GPU_depth_test(GPU_DEPTH_ALWAYS);
|
||||
GPU_depth_mask(true);
|
||||
}
|
||||
else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) {
|
||||
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
|
||||
GPU_depth_mask(true);
|
||||
GPU_clear_depth(1.0f);
|
||||
}
|
||||
else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) {
|
||||
GPU_depth_test(GPU_DEPTH_EQUAL);
|
||||
GPU_depth_mask(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool gpu_select_query_load_id(uint id)
|
||||
{
|
||||
if (g_query_state.query_issued) {
|
||||
g_query_state.queries->end_query();
|
||||
}
|
||||
|
||||
g_query_state.queries->begin_query();
|
||||
g_query_state.ids->append(id);
|
||||
g_query_state.query_issued = true;
|
||||
|
||||
if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS) {
|
||||
/* Second pass should never run if first pass fails,
|
||||
* can read past `buffer_len` in this case. */
|
||||
BLI_assert(g_query_state.oldhits != -1);
|
||||
if (g_query_state.index < g_query_state.oldhits) {
|
||||
if (g_query_state.buffer->storage[g_query_state.index].id == id) {
|
||||
g_query_state.index++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint gpu_select_query_end()
|
||||
{
|
||||
uint hits = 0;
|
||||
|
||||
if (g_query_state.query_issued) {
|
||||
g_query_state.queries->end_query();
|
||||
}
|
||||
|
||||
Span<uint> ids = *g_query_state.ids;
|
||||
Vector<uint32_t, QUERY_MIN_LEN> result(ids.size());
|
||||
g_query_state.queries->get_occlusion_result(result);
|
||||
|
||||
for (int i = 0; i < result.size(); i++) {
|
||||
if (result[i] != 0) {
|
||||
if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) {
|
||||
GPUSelectResult hit_result{};
|
||||
hit_result.id = ids[i];
|
||||
hit_result.depth = 0xFFFF;
|
||||
g_query_state.buffer->storage.append(hit_result);
|
||||
hits++;
|
||||
}
|
||||
else {
|
||||
int j;
|
||||
/* search in buffer and make selected object first */
|
||||
for (j = 0; j < g_query_state.oldhits; j++) {
|
||||
if (g_query_state.buffer->storage[j].id == ids[i]) {
|
||||
g_query_state.buffer->storage[j].depth = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete g_query_state.queries;
|
||||
delete g_query_state.ids;
|
||||
|
||||
GPU_write_mask(g_query_state.write_mask);
|
||||
GPU_depth_test(g_query_state.depth_test);
|
||||
GPU_viewport(UNPACK4(g_query_state.viewport));
|
||||
|
||||
GPU_debug_group_end();
|
||||
|
||||
return hits;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1235
blender-5.2.0/source/blender/gpu/intern/gpu_shader.cc
Normal file
1235
blender-5.2.0/source/blender/gpu/intern/gpu_shader.cc
Normal file
File diff suppressed because it is too large
Load Diff
325
blender-5.2.0/source/blender/gpu/intern/gpu_shader_builtin.cc
Normal file
325
blender-5.2.0/source/blender/gpu/intern/gpu_shader_builtin.cc
Normal file
@@ -0,0 +1,325 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "GPU_shader_builtin.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BuiltinShader : gpu::StaticShader {
|
||||
/* WORKAROUND: This is needed for the polyline workaround default initialization. */
|
||||
bool init = false;
|
||||
|
||||
BuiltinShader(std::string info_name) : gpu::StaticShader(info_name) {}
|
||||
};
|
||||
|
||||
/* Cache of built-in shaders (each is created on first use). */
|
||||
static BuiltinShader *builtin_shaders[GPU_SHADER_CFG_LEN][GPU_SHADER_BUILTIN_LEN] = {{nullptr}};
|
||||
|
||||
static const char *builtin_shader_create_info_name(GPUBuiltinShader shader)
|
||||
{
|
||||
switch (shader) {
|
||||
case GPU_SHADER_TEXT:
|
||||
return "gpu_shader_text";
|
||||
case GPU_SHADER_KEYFRAME_SHAPE:
|
||||
return "gpu_shader_keyframe_shape";
|
||||
case GPU_SHADER_SIMPLE_LIGHTING:
|
||||
return "gpu_shader_simple_lighting";
|
||||
case GPU_SHADER_3D_IMAGE:
|
||||
return "gpu_shader_3D_image";
|
||||
case GPU_SHADER_3D_IMAGE_SCENE_LINEAR_TO_REC709_SRGB:
|
||||
return "gpu_shader_3D_image_scene_linear";
|
||||
case GPU_SHADER_3D_IMAGE_COLOR:
|
||||
return "gpu_shader_3D_image_color";
|
||||
case GPU_SHADER_3D_IMAGE_COLOR_SCENE_LINEAR_TO_REC709_SRGB:
|
||||
return "gpu_shader_3D_image_color_scene_linear";
|
||||
case GPU_SHADER_2D_CHECKER:
|
||||
return "gpu_shader_2D_checker";
|
||||
case GPU_SHADER_2D_DIAG_STRIPES:
|
||||
return "gpu_shader_2D_diag_stripes";
|
||||
case GPU_SHADER_ICON:
|
||||
return "gpu_shader_icon";
|
||||
case GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE:
|
||||
return "gpu_shader_2D_image_overlays_merge";
|
||||
case GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE:
|
||||
return "gpu_shader_2D_image_overlays_stereo_merge";
|
||||
case GPU_SHADER_2D_IMAGE_DESATURATE_COLOR:
|
||||
return "gpu_shader_2D_image_desaturate_color";
|
||||
case GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR:
|
||||
return "gpu_shader_2D_image_shuffle_color";
|
||||
case GPU_SHADER_2D_IMAGE_RECT_COLOR:
|
||||
return "gpu_shader_2D_image_rect_color";
|
||||
case GPU_SHADER_ICON_MULTI:
|
||||
return "gpu_shader_icon_multi";
|
||||
case GPU_SHADER_3D_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_uniform_color";
|
||||
case GPU_SHADER_3D_FLAT_COLOR:
|
||||
return "gpu_shader_3D_flat_color";
|
||||
case GPU_SHADER_3D_SMOOTH_COLOR:
|
||||
return "gpu_shader_3D_smooth_color";
|
||||
case GPU_SHADER_3D_DEPTH_ONLY:
|
||||
return "gpu_shader_3D_depth_only";
|
||||
case GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_clipped_uniform_color";
|
||||
case GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_polyline_uniform_color";
|
||||
case GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_polyline_uniform_color_clipped";
|
||||
case GPU_SHADER_3D_POLYLINE_FLAT_COLOR:
|
||||
return "gpu_shader_3D_polyline_flat_color";
|
||||
case GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR:
|
||||
return "gpu_shader_3D_polyline_smooth_color";
|
||||
case GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_line_dashed_uniform_color";
|
||||
case GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA:
|
||||
return "gpu_shader_2D_point_uniform_size_uniform_color_aa";
|
||||
case GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA:
|
||||
return "gpu_shader_2D_point_uniform_size_uniform_color_outline_aa";
|
||||
case GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR:
|
||||
return "gpu_shader_3D_point_varying_size_varying_color";
|
||||
case GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA:
|
||||
return "gpu_shader_3D_point_uniform_size_uniform_color_aa";
|
||||
case GPU_SHADER_3D_POINT_FLAT_COLOR:
|
||||
return "gpu_shader_3D_point_flat_color";
|
||||
case GPU_SHADER_3D_POINT_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_point_uniform_color";
|
||||
case GPU_SHADER_2D_AREA_BORDERS:
|
||||
return "gpu_shader_2D_area_borders";
|
||||
case GPU_SHADER_2D_WIDGET_BASE:
|
||||
return "gpu_shader_2D_widget_base";
|
||||
case GPU_SHADER_2D_WIDGET_BASE_INST:
|
||||
return "gpu_shader_2D_widget_base_inst";
|
||||
case GPU_SHADER_2D_WIDGET_SHADOW:
|
||||
return "gpu_shader_2D_widget_shadow";
|
||||
case GPU_SHADER_2D_NODE_SOCKET:
|
||||
return "gpu_shader_2D_node_socket";
|
||||
case GPU_SHADER_2D_NODE_SOCKET_INST:
|
||||
return "gpu_shader_2D_node_socket_inst";
|
||||
case GPU_SHADER_2D_NODELINK:
|
||||
return "gpu_shader_2D_nodelink";
|
||||
case GPU_SHADER_GPENCIL_STROKE:
|
||||
return "gpu_shader_gpencil_stroke";
|
||||
case GPU_SHADER_SEQUENCER_STRIPS:
|
||||
return "gpu_shader_sequencer_strips";
|
||||
case GPU_SHADER_SEQUENCER_THUMBS:
|
||||
return "gpu_shader_sequencer_thumbs";
|
||||
case GPU_SHADER_SEQUENCER_SCOPE_RASTER:
|
||||
return "gpu_shader_sequencer_scope_raster";
|
||||
case GPU_SHADER_SEQUENCER_SCOPE_RESOLVE:
|
||||
return "gpu_shader_sequencer_scope_resolve";
|
||||
case GPU_SHADER_SEQUENCER_ZEBRA:
|
||||
return "gpu_shader_sequencer_zebra";
|
||||
case GPU_SHADER_INDEXBUF_POINTS:
|
||||
return "gpu_shader_index_2d_array_points";
|
||||
case GPU_SHADER_INDEXBUF_LINES:
|
||||
return "gpu_shader_index_2d_array_lines";
|
||||
case GPU_SHADER_INDEXBUF_TRIS:
|
||||
return "gpu_shader_index_2d_array_tris";
|
||||
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8:
|
||||
return "gpu_shader_2D_update_mipmaps_unorm_8";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8_LAYERED:
|
||||
return "gpu_shader_2D_update_mipmaps_unorm_8_layered";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8_8_8_8:
|
||||
return "gpu_shader_2D_update_mipmaps_unorm_8_8_8_8";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_UNORM_8_8_8_8_LAYERED:
|
||||
return "gpu_shader_2D_update_mipmaps_unorm_8_8_8_8_layered";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_16";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16_LAYERED:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_16_layered";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16_16_16_16:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_16_16_16_16";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_16_16_16_16_LAYERED:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_16_16_16_16_layered";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_32";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32_LAYERED:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_32_layered";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32_32_32_32:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_32_32_32_32";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SFLOAT_32_32_32_32_LAYERED:
|
||||
return "gpu_shader_2D_update_mipmaps_sfloat_32_32_32_32_layered";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SRGBA_8_8_8_8:
|
||||
return "gpu_shader_2D_update_mipmaps_srgba_8_8_8_8";
|
||||
case GPU_SHADER_2D_UPDATE_MIPMAPS_SRGBA_8_8_8_8_LAYERED:
|
||||
return "gpu_shader_2D_update_mipmaps_srgba_8_8_8_8_layered";
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *builtin_shader_create_info_name_clipped(GPUBuiltinShader shader)
|
||||
{
|
||||
switch (shader) {
|
||||
case GPU_SHADER_3D_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_uniform_color_clipped";
|
||||
case GPU_SHADER_3D_FLAT_COLOR:
|
||||
return "gpu_shader_3D_flat_color_clipped";
|
||||
case GPU_SHADER_3D_SMOOTH_COLOR:
|
||||
return "gpu_shader_3D_smooth_color_clipped";
|
||||
case GPU_SHADER_3D_DEPTH_ONLY:
|
||||
return "gpu_shader_3D_depth_only_clipped";
|
||||
case GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_line_dashed_uniform_color_clipped";
|
||||
case GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA:
|
||||
return "gpu_shader_3D_point_uniform_size_uniform_color_aa_clipped";
|
||||
case GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR:
|
||||
return "gpu_shader_3D_polyline_uniform_color_clipped";
|
||||
default:
|
||||
BLI_assert_msg(false, "Clipped shader configuration not available.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Shader *GPU_shader_get_builtin_shader_with_config(GPUBuiltinShader shader,
|
||||
GPUShaderConfig sh_cfg)
|
||||
{
|
||||
BLI_assert(shader < GPU_SHADER_BUILTIN_LEN);
|
||||
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#endif
|
||||
BuiltinShader **sh_p = &builtin_shaders[sh_cfg][shader];
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
if (*sh_p == nullptr) {
|
||||
if (sh_cfg == GPU_SHADER_CFG_DEFAULT) {
|
||||
/* Common case. */
|
||||
const char *info_name = builtin_shader_create_info_name(shader);
|
||||
*sh_p = MEM_new<BuiltinShader>(__func__, info_name);
|
||||
}
|
||||
else if (sh_cfg == GPU_SHADER_CFG_CLIPPED) {
|
||||
/* In rare cases geometry shaders calculate clipping themselves. */
|
||||
const char *info_name_clipped = builtin_shader_create_info_name_clipped(shader);
|
||||
if (!StringRefNull(info_name_clipped).is_empty()) {
|
||||
*sh_p = MEM_new<BuiltinShader>(__func__, info_name_clipped);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((*sh_p)->init == false) {
|
||||
(*sh_p)->init = true;
|
||||
if (ELEM(shader,
|
||||
GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR,
|
||||
GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR,
|
||||
GPU_SHADER_3D_POLYLINE_FLAT_COLOR,
|
||||
GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR))
|
||||
{
|
||||
gpu::Shader *sh = (*sh_p)->get();
|
||||
/* Set a default value for `lineSmooth`.
|
||||
* Ideally this value should be set by the caller. */
|
||||
GPU_shader_bind(sh);
|
||||
GPU_shader_uniform_1i(sh, "lineSmooth", 1);
|
||||
/* WORKAROUND: See is_polyline declaration. */
|
||||
sh->is_polyline = true;
|
||||
}
|
||||
}
|
||||
|
||||
return (*sh_p)->get();
|
||||
}
|
||||
|
||||
static void gpu_shader_warm_builtin_shader_async(GPUBuiltinShader shader, GPUShaderConfig sh_cfg)
|
||||
{
|
||||
BLI_assert(shader < GPU_SHADER_BUILTIN_LEN);
|
||||
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#endif
|
||||
BuiltinShader **sh_p = &builtin_shaders[sh_cfg][shader];
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
if (*sh_p == nullptr) {
|
||||
if (sh_cfg == GPU_SHADER_CFG_DEFAULT) {
|
||||
/* Common case. */
|
||||
const char *info_name = builtin_shader_create_info_name(shader);
|
||||
*sh_p = MEM_new<BuiltinShader>(__func__, info_name);
|
||||
}
|
||||
else if (sh_cfg == GPU_SHADER_CFG_CLIPPED) {
|
||||
/* In rare cases geometry shaders calculate clipping themselves. */
|
||||
const char *info_name_clipped = builtin_shader_create_info_name_clipped(shader);
|
||||
if (!StringRefNull(info_name_clipped).is_empty()) {
|
||||
*sh_p = MEM_new<BuiltinShader>(__func__, info_name_clipped);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
(*sh_p)->ensure_compile_async();
|
||||
}
|
||||
|
||||
gpu::Shader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader)
|
||||
{
|
||||
return GPU_shader_get_builtin_shader_with_config(shader, GPU_SHADER_CFG_DEFAULT);
|
||||
}
|
||||
|
||||
void GPU_shader_builtin_warm_up()
|
||||
{
|
||||
if ((G.debug & G_DEBUG_GPU) && (GPU_backend_get_type() == GPU_BACKEND_OPENGL)) {
|
||||
/* On some system (Mesa OpenGL), doing this warm up seems to breaks something related to debug
|
||||
* hooks and makes the Blender application hang. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (GPU_use_subprocess_compilation() && (GPU_backend_get_type() == GPU_BACKEND_OPENGL)) {
|
||||
/* The overhead of creating the subprocesses at this exact moment can create bubbles during the
|
||||
* startup process. It is usually fast enough on OpenGL that we can skip it. */
|
||||
return;
|
||||
}
|
||||
/* Ordered by first usage in default startup screen.
|
||||
* Adding more to this list will delay the scheduling of engine shaders and increase time to
|
||||
* first pixel. */
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_TEXT, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_WIDGET_BASE, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_3D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR,
|
||||
GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_3D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_NODE_SOCKET, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_WIDGET_BASE_INST, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR,
|
||||
GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR,
|
||||
GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR,
|
||||
GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_WIDGET_SHADOW, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_DIAG_STRIPES, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_IMAGE_RECT_COLOR, GPU_SHADER_CFG_DEFAULT);
|
||||
gpu_shader_warm_builtin_shader_async(GPU_SHADER_2D_AREA_BORDERS, GPU_SHADER_CFG_DEFAULT);
|
||||
}
|
||||
|
||||
void GPU_shader_free_builtin_shaders()
|
||||
{
|
||||
/* Make sure non is bound before deleting. */
|
||||
GPU_shader_unbind();
|
||||
for (int i = 0; i < GPU_SHADER_CFG_LEN; i++) {
|
||||
for (int j = 0; j < GPU_SHADER_BUILTIN_LEN; j++) {
|
||||
MEM_SAFE_DELETE(builtin_shaders[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,875 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Descriptor type used to define shader structure, resources and interfaces.
|
||||
*/
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_platform.hh"
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_create_info_private.hh"
|
||||
#include "gpu_shader_dependency_private.hh"
|
||||
|
||||
#undef GPU_SHADER_NAMED_INTERFACE_INFO
|
||||
#undef GPU_SHADER_INTERFACE_INFO
|
||||
#undef GPU_SHADER_CREATE_INFO
|
||||
#undef GPU_SHADER_NAMED_INTERFACE_END
|
||||
#undef GPU_SHADER_INTERFACE_END
|
||||
#undef GPU_SHADER_CREATE_END
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu::shader {
|
||||
|
||||
using CreateInfoDictionary = Map<StringRef, ShaderCreateInfo *>;
|
||||
using InterfaceDictionary = Map<StringRef, StageInterfaceInfo *>;
|
||||
|
||||
static CreateInfoDictionary *g_create_infos = nullptr;
|
||||
static InterfaceDictionary *g_interfaces = nullptr;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Check Backend Support
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static bool is_vulkan_compatible_interface(const StageInterfaceInfo &iface)
|
||||
{
|
||||
if (iface.instance_name.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool use_flat = false;
|
||||
bool use_smooth = false;
|
||||
bool use_noperspective = false;
|
||||
for (const StageInterfaceInfo::InOut &attr : iface.inouts) {
|
||||
switch (attr.interp) {
|
||||
case Interpolation::FLAT:
|
||||
use_flat = true;
|
||||
break;
|
||||
case Interpolation::SMOOTH:
|
||||
use_smooth = true;
|
||||
break;
|
||||
case Interpolation::NO_PERSPECTIVE:
|
||||
use_noperspective = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int num_used_interpolation_types = (use_flat ? 1 : 0) + (use_smooth ? 1 : 0) +
|
||||
(use_noperspective ? 1 : 0);
|
||||
|
||||
#if 0
|
||||
if (num_used_interpolation_types > 1) {
|
||||
std::cout << "'" << iface.name << "' uses multiple interpolation types\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
return num_used_interpolation_types <= 1;
|
||||
}
|
||||
|
||||
bool ShaderCreateInfo::is_vulkan_compatible() const
|
||||
{
|
||||
/* Vulkan doesn't support setting an interpolation mode per attribute in a struct. */
|
||||
for (const StageInterfaceInfo *iface : vertex_out_interfaces_) {
|
||||
if (!is_vulkan_compatible_interface(*iface)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const StageInterfaceInfo *iface : geometry_out_interfaces_) {
|
||||
if (!is_vulkan_compatible_interface(*iface)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ShaderCreateInfo::buffer_typename(StringRefNull type_name, bool uniform_buffer) const
|
||||
{
|
||||
if (bool(this->builtins_ & BuiltinBits::NO_BUFFER_TYPE_LINTING) || type_name.startswith("int") ||
|
||||
type_name.startswith("uint") || type_name.startswith("float") ||
|
||||
type_name.startswith("packed_"))
|
||||
{
|
||||
return type_name;
|
||||
}
|
||||
return type_name + "_host_shared_" + (uniform_buffer ? "uniform_" : "");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
ShaderCreateInfo::ShaderCreateInfo(const char *name) : name_(name)
|
||||
{
|
||||
bool last_char_is_underscore = false;
|
||||
/* Escape the shader name to be able to use it inside an identifier. */
|
||||
for (char &c : name_) {
|
||||
if (!std::isalnum(c)) {
|
||||
c = '_';
|
||||
}
|
||||
if (c == '_' && last_char_is_underscore) {
|
||||
/* Avoid GLSL warning about double underscore being reserved. */
|
||||
c = 'w';
|
||||
}
|
||||
last_char_is_underscore = c == '_';
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShaderCreateInfo::resource_guard_defines(Span<CompilationConstant> constants) const
|
||||
{
|
||||
std::string defines;
|
||||
defines += "#define CREATE_INFO_" + name_ + "\n";
|
||||
for (const auto &additional_info : additional_infos_) {
|
||||
const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(
|
||||
gpu_shader_create_info_get(additional_info.name.c_str()));
|
||||
|
||||
if (additional_info.conditions.evaluate(constants)) {
|
||||
defines += info.resource_guard_defines(constants);
|
||||
}
|
||||
}
|
||||
return defines;
|
||||
}
|
||||
|
||||
void ShaderCreateInfo::finalize(const bool recursive)
|
||||
{
|
||||
if (finalized_) {
|
||||
return;
|
||||
}
|
||||
finalized_ = true;
|
||||
|
||||
auto deps_merged = std::make_unique<Set<StringRefNull>>();
|
||||
|
||||
validate_vertex_attributes();
|
||||
|
||||
for (const auto &additional_info : additional_infos_) {
|
||||
|
||||
/* Fetch create info. */
|
||||
const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(
|
||||
gpu_shader_create_info_get(additional_info.name.c_str()));
|
||||
|
||||
if (recursive) {
|
||||
const_cast<ShaderCreateInfo &>(info).finalize(recursive);
|
||||
}
|
||||
else {
|
||||
BLI_assert(info.finalized_);
|
||||
}
|
||||
|
||||
interface_names_size_ += info.interface_names_size_;
|
||||
|
||||
/* NOTE: EEVEE Materials can result in nested includes. To avoid duplicate
|
||||
* shader resources, we need to avoid inserting duplicates.
|
||||
* TODO: Optimize create info preparation to include each individual "additional_info"
|
||||
* only a single time. */
|
||||
vertex_inputs_.extend_non_duplicates(info.vertex_inputs_);
|
||||
fragment_outputs_.extend_non_duplicates(info.fragment_outputs_);
|
||||
vertex_out_interfaces_.extend_non_duplicates(info.vertex_out_interfaces_);
|
||||
geometry_out_interfaces_.extend_non_duplicates(info.geometry_out_interfaces_);
|
||||
subpass_inputs_.extend_non_duplicates(info.subpass_inputs_);
|
||||
specialization_constants_.extend_non_duplicates(info.specialization_constants_);
|
||||
compilation_constants_.extend_non_duplicates(info.compilation_constants_);
|
||||
|
||||
shared_variables_.extend(info.shared_variables_);
|
||||
|
||||
validate_vertex_attributes(&info);
|
||||
|
||||
/* Insert with duplicate check. */
|
||||
push_constants_.extend_non_duplicates(info.push_constants_);
|
||||
defines_.extend_non_duplicates(info.defines_);
|
||||
typedef_sources_.extend_non_duplicates(info.typedef_sources_);
|
||||
|
||||
for (const auto &res : info.pass_resources_) {
|
||||
extend_predicate(pass_resources_, res, additional_info.conditions);
|
||||
}
|
||||
for (const auto &res : info.batch_resources_) {
|
||||
extend_predicate(batch_resources_, res, additional_info.conditions);
|
||||
}
|
||||
for (const auto &res : info.geometry_resources_) {
|
||||
extend_predicate(geometry_resources_, res, additional_info.conditions);
|
||||
}
|
||||
|
||||
/* API-specific parameters.
|
||||
* We will only copy API-specific parameters if they are otherwise unassigned. */
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
if (mtl_max_threads_per_threadgroup_ == 0) {
|
||||
mtl_max_threads_per_threadgroup_ = info.mtl_max_threads_per_threadgroup_;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (info.early_fragment_test_) {
|
||||
early_fragment_test_ = true;
|
||||
depth_write_ = DepthWrite::UNCHANGED;
|
||||
}
|
||||
/* Modify depth write if has been changed from default.
|
||||
* `UNCHANGED` implies gl_FragDepth is not used at all. */
|
||||
if (info.depth_write_ != DepthWrite::UNCHANGED) {
|
||||
depth_write_ = info.depth_write_;
|
||||
}
|
||||
|
||||
/* Inherit builtin bits from additional info. */
|
||||
builtins_ |= info.builtins_;
|
||||
|
||||
/* TODO(fclem): We need to reintroduce this check before compiling.
|
||||
* The issue is that the new SRT paradigm allows for conflicting resources if they are not
|
||||
* defined at the same time (using compilation constants). */
|
||||
// validate_merge(info);
|
||||
|
||||
if (!deps_merged->add(info.name_)) {
|
||||
assert_no_overlap(info, false, "additional info already merged via another info");
|
||||
}
|
||||
|
||||
if (info.compute_layout_.local_size_x != -1) {
|
||||
assert_no_overlap(
|
||||
info, compute_layout_.local_size_x == -1, "Compute layout already defined");
|
||||
compute_layout_ = info.compute_layout_;
|
||||
}
|
||||
|
||||
if (!info.vertex_source_.is_empty()) {
|
||||
assert_no_overlap(info, vertex_source_.is_empty(), "Vertex source already existing");
|
||||
vertex_source_ = info.vertex_source_;
|
||||
}
|
||||
if (!info.geometry_source_.is_empty()) {
|
||||
assert_no_overlap(info, geometry_source_.is_empty(), "Geometry source already existing");
|
||||
geometry_source_ = info.geometry_source_;
|
||||
geometry_layout_ = info.geometry_layout_;
|
||||
}
|
||||
if (!info.fragment_source_.is_empty()) {
|
||||
assert_no_overlap(info, fragment_source_.is_empty(), "Fragment source already existing");
|
||||
fragment_source_ = info.fragment_source_;
|
||||
}
|
||||
if (!info.compute_source_.is_empty()) {
|
||||
assert_no_overlap(info, compute_source_.is_empty(), "Compute source already existing");
|
||||
compute_source_ = info.compute_source_;
|
||||
}
|
||||
|
||||
if (info.vertex_entry_fn_ != "main") {
|
||||
assert_no_overlap(info, vertex_entry_fn_ == "main", "Vertex function already existing");
|
||||
vertex_entry_fn_ = info.vertex_entry_fn_;
|
||||
}
|
||||
if (info.geometry_entry_fn_ != "main") {
|
||||
assert_no_overlap(info, geometry_entry_fn_ == "main", "Geometry function already existing");
|
||||
geometry_entry_fn_ = info.geometry_entry_fn_;
|
||||
}
|
||||
if (info.fragment_entry_fn_ != "main") {
|
||||
assert_no_overlap(info, fragment_entry_fn_ == "main", "Fragment function already existing");
|
||||
fragment_entry_fn_ = info.fragment_entry_fn_;
|
||||
}
|
||||
if (info.compute_entry_fn_ != "main") {
|
||||
assert_no_overlap(info, compute_entry_fn_ == "main", "Compute function already existing");
|
||||
compute_entry_fn_ = info.compute_entry_fn_;
|
||||
}
|
||||
}
|
||||
|
||||
if (!geometry_source_.is_empty() && bool(builtins_ & BuiltinBits::LAYER)) {
|
||||
std::cout << name_
|
||||
<< ": Validation failed. BuiltinBits::LAYER shouldn't be used with geometry shaders."
|
||||
<< std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
|
||||
if (auto_resource_location_) {
|
||||
int images = 0, samplers = 0, ubos = 0, ssbos = 0;
|
||||
|
||||
for (auto &res : batch_resources_) {
|
||||
set_resource_slot(res, images, samplers, ubos, ssbos);
|
||||
}
|
||||
for (auto &res : pass_resources_) {
|
||||
set_resource_slot(res, images, samplers, ubos, ssbos);
|
||||
}
|
||||
for (auto &res : geometry_resources_) {
|
||||
set_resource_slot(res, images, samplers, ubos, ssbos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderCreateInfo::extend_predicate(Vector<Resource, 0> &resource_vector,
|
||||
ShaderCreateInfo::Resource res_copy,
|
||||
Span<ConditionFn> additional_conditions) const
|
||||
{
|
||||
res_copy.conditions.extend(additional_conditions);
|
||||
/** IMPORTANT: We keep duplicates until we evaluate the conditions. */
|
||||
resource_vector.append(res_copy);
|
||||
};
|
||||
|
||||
void ShaderCreateInfo::assert_no_overlap(const ShaderCreateInfo &info,
|
||||
const bool test,
|
||||
const StringRefNull error) const
|
||||
{
|
||||
if (!test) {
|
||||
std::cout << name_ << ": Validation failed while merging " << info.name_ << " : ";
|
||||
std::cout << error << std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderCreateInfo::set_resource_slot(
|
||||
Resource &res, int &images, int &samplers, int &ubos, int &ssbos) const
|
||||
{
|
||||
switch (res.bind_type) {
|
||||
case Resource::BindType::UNIFORM_BUFFER:
|
||||
res.slot = ubos++;
|
||||
break;
|
||||
case Resource::BindType::STORAGE_BUFFER:
|
||||
res.slot = ssbos++;
|
||||
break;
|
||||
case Resource::BindType::SAMPLER:
|
||||
res.slot = samplers++;
|
||||
break;
|
||||
case Resource::BindType::IMAGE:
|
||||
res.slot = images++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShaderCreateInfo::check_error() const
|
||||
{
|
||||
std::string error;
|
||||
|
||||
/* At least a vertex shader and a fragment shader are required, or only a compute shader. */
|
||||
if (this->compute_source_.is_empty()) {
|
||||
if (this->vertex_source_.is_empty()) {
|
||||
error += "Missing vertex shader in " + this->name_ + ".\n";
|
||||
}
|
||||
if (this->fragment_source_.is_empty()) {
|
||||
error += "Missing fragment shader in " + this->name_ + ".\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!this->vertex_source_.is_empty()) {
|
||||
error += "Compute shader has vertex_source_ shader attached in " + this->name_ + ".\n";
|
||||
}
|
||||
if (!this->geometry_source_.is_empty()) {
|
||||
error += "Compute shader has geometry_source_ shader attached in " + this->name_ + ".\n";
|
||||
}
|
||||
if (!this->fragment_source_.is_empty()) {
|
||||
error += "Compute shader has fragment_source_ shader attached in " + this->name_ + ".\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->geometry_source_.is_empty()) {
|
||||
if (flag_is_set(this->builtins_, BuiltinBits::BARYCENTRIC_COORD)) {
|
||||
error += "Shader " + this->name_ +
|
||||
" has geometry stage and uses barycentric coordinates. This is not allowed as "
|
||||
"fallback injects a geometry stage.\n";
|
||||
}
|
||||
if (flag_is_set(this->builtins_, BuiltinBits::VIEWPORT_INDEX)) {
|
||||
error += "Shader " + this->name_ +
|
||||
" has geometry stage and uses multi-viewport. This is not allowed as "
|
||||
"fallback injects a geometry stage.\n";
|
||||
}
|
||||
if (flag_is_set(this->builtins_, BuiltinBits::LAYER)) {
|
||||
error += "Shader " + this->name_ +
|
||||
" has geometry stage and uses layer output. This is not allowed as "
|
||||
"fallback injects a geometry stage.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ((G.debug & G_DEBUG_GPU) == 0) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (flag_is_set(this->builtins_,
|
||||
BuiltinBits::BARYCENTRIC_COORD | BuiltinBits::VIEWPORT_INDEX |
|
||||
BuiltinBits::LAYER))
|
||||
{
|
||||
for (const StageInterfaceInfo *interface : this->vertex_out_interfaces_) {
|
||||
if (interface->instance_name.is_empty()) {
|
||||
error += "Shader " + this->name_ + " uses interface " + interface->name +
|
||||
" that doesn't contain an instance name, but is required for the fallback "
|
||||
"geometry shader.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const StageInterfaceInfo *interface : this->vertex_out_interfaces_) {
|
||||
for (const StageInterfaceInfo::InOut &inout : interface->inouts) {
|
||||
if (inout.name.is_array()) {
|
||||
error += "Shader " + this->name_ + " : \"" + interface->name + "." + inout.name + "\":";
|
||||
error += " Array types are not allowed in shader stage interfaces.\n";
|
||||
}
|
||||
if (ELEM(inout.type, Type::float3x3_t, Type::float4x4_t)) {
|
||||
error += "Shader " + this->name_ + " : \"" + interface->name + "." + inout.name + "\":";
|
||||
error += " Matrix types are not allowed in shader stage interfaces.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->is_vulkan_compatible()) {
|
||||
error += this->name_ +
|
||||
" contains a stage interface using an instance name and mixed interpolation modes. "
|
||||
"This is not compatible with Vulkan and need to be adjusted.\n";
|
||||
}
|
||||
|
||||
/* Validate specialization constants. */
|
||||
for (int i = 0; i < specialization_constants_.size(); i++) {
|
||||
for (int j = i + 1; j < specialization_constants_.size(); j++) {
|
||||
if (specialization_constants_[i].name == specialization_constants_[j].name) {
|
||||
error += this->name_ + " contains two specialization constants with the name: " +
|
||||
std::string(specialization_constants_[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate compilation constants. */
|
||||
for (int i = 0; i < compilation_constants_.size(); i++) {
|
||||
for (int j = i + 1; j < compilation_constants_.size(); j++) {
|
||||
if (compilation_constants_[i].name == compilation_constants_[j].name) {
|
||||
error += this->name_ + " contains two compilation constants with the name: " +
|
||||
std::string(compilation_constants_[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate shared variables. */
|
||||
for (int i = 0; i < shared_variables_.size(); i++) {
|
||||
for (int j = i + 1; j < shared_variables_.size(); j++) {
|
||||
if (shared_variables_[i].name == shared_variables_[j].name) {
|
||||
error += this->name_ + " contains two specialization constants with the name: " +
|
||||
std::string(shared_variables_[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check same bind-points usage. */
|
||||
Set<int> images, samplers, ubos, ssbos;
|
||||
|
||||
auto register_resource = [&](const Resource &res) -> bool {
|
||||
switch (res.bind_type) {
|
||||
case Resource::BindType::UNIFORM_BUFFER:
|
||||
return ubos.add(res.slot);
|
||||
case Resource::BindType::STORAGE_BUFFER:
|
||||
return ssbos.add(res.slot);
|
||||
case Resource::BindType::SAMPLER:
|
||||
return samplers.add(res.slot);
|
||||
case Resource::BindType::IMAGE:
|
||||
return images.add(res.slot);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto print_error_msg = [&](const Resource &res, const Vector<Resource> &resources) {
|
||||
auto print_resource_name = [&](const Resource &res) {
|
||||
switch (res.bind_type) {
|
||||
case Resource::BindType::UNIFORM_BUFFER:
|
||||
error += "Uniform Buffer " + res.uniformbuf.name;
|
||||
break;
|
||||
case Resource::BindType::STORAGE_BUFFER:
|
||||
error += "Storage Buffer " + res.storagebuf.name;
|
||||
break;
|
||||
case Resource::BindType::SAMPLER:
|
||||
error += "Sampler " + res.sampler.name;
|
||||
break;
|
||||
case Resource::BindType::IMAGE:
|
||||
error += "Image " + res.image.name;
|
||||
break;
|
||||
default:
|
||||
error += "Unknown Type";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
for (const Resource &_res : resources) {
|
||||
if (&res != &_res && res.bind_type == _res.bind_type && res.slot == _res.slot) {
|
||||
error += name_ + ": Validation failed : Overlapping ";
|
||||
print_resource_name(res);
|
||||
error += " and ";
|
||||
print_resource_name(_res);
|
||||
error += " at binding location " + std::to_string(res.slot) + "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto &res : batch_resources_) {
|
||||
if (register_resource(res) == false) {
|
||||
print_error_msg(res, resources_get_all_());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &res : pass_resources_) {
|
||||
if (register_resource(res) == false) {
|
||||
print_error_msg(res, resources_get_all_());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &res : geometry_resources_) {
|
||||
if (register_resource(res) == false) {
|
||||
print_error_msg(res, resources_get_all_());
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void ShaderCreateInfo::validate_merge(const ShaderCreateInfo &other_info)
|
||||
{
|
||||
if (!auto_resource_location_) {
|
||||
/* Check same bind-points usage in OGL. */
|
||||
Set<int> images, samplers, ubos, ssbos;
|
||||
|
||||
auto register_resource = [&](const Resource &res) -> bool {
|
||||
switch (res.bind_type) {
|
||||
case Resource::BindType::UNIFORM_BUFFER:
|
||||
return images.add(res.slot);
|
||||
case Resource::BindType::STORAGE_BUFFER:
|
||||
return samplers.add(res.slot);
|
||||
case Resource::BindType::SAMPLER:
|
||||
return ubos.add(res.slot);
|
||||
case Resource::BindType::IMAGE:
|
||||
return ssbos.add(res.slot);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto print_error_msg = [&](const Resource &res, const Vector<Resource> &resources) {
|
||||
auto print_resource_name = [&](const Resource &res) {
|
||||
switch (res.bind_type) {
|
||||
case Resource::BindType::UNIFORM_BUFFER:
|
||||
std::cout << "Uniform Buffer " << res.uniformbuf.name;
|
||||
break;
|
||||
case Resource::BindType::STORAGE_BUFFER:
|
||||
std::cout << "Storage Buffer " << res.storagebuf.name;
|
||||
break;
|
||||
case Resource::BindType::SAMPLER:
|
||||
std::cout << "Sampler " << res.sampler.name;
|
||||
break;
|
||||
case Resource::BindType::IMAGE:
|
||||
std::cout << "Image " << res.image.name;
|
||||
break;
|
||||
default:
|
||||
std::cout << "Unknown Type";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
for (const Resource &_res : resources) {
|
||||
if (&res != &_res && res.bind_type == _res.bind_type && res.slot == _res.slot) {
|
||||
std::cout << name_ << ": Validation failed : Overlapping ";
|
||||
print_resource_name(res);
|
||||
std::cout << " and ";
|
||||
print_resource_name(_res);
|
||||
std::cout << " at (" << res.slot << ") while merging " << other_info.name_ << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (auto &res : batch_resources_) {
|
||||
if (register_resource(res) == false) {
|
||||
print_error_msg(res, resources_get_all_());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &res : pass_resources_) {
|
||||
if (register_resource(res) == false) {
|
||||
print_error_msg(res, resources_get_all_());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &res : geometry_resources_) {
|
||||
if (register_resource(res) == false) {
|
||||
print_error_msg(res, resources_get_all_());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderCreateInfo::validate_vertex_attributes(const ShaderCreateInfo *other_info)
|
||||
{
|
||||
uint32_t attr_bits = 0;
|
||||
for (auto &attr : vertex_inputs_) {
|
||||
if (attr.type == Type::float3x3_t) {
|
||||
std::cout << name_ << ": \"" << attr.name << "\" : float3x3 unsupported as vertex attribute."
|
||||
<< std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
if (attr.type == Type::float4x4_t) {
|
||||
std::cout << name_ << ": \"" << attr.name << "\" : float4x4 unsupported as vertex attribute."
|
||||
<< std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
if (attr.name.is_array()) {
|
||||
std::cout << name_ << ": \"" << attr.name
|
||||
<< "\" : arrays are unsupported as vertex attribute." << std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
if (attr.index >= 16 || attr.index < 0) {
|
||||
std::cout << name_ << ": Invalid index for attribute \"" << attr.name << "\"" << std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
uint32_t attr_new = 0;
|
||||
if (attr.type == Type::float4x4_t) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
attr_new |= 1 << (attr.index + i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
attr_new |= 1 << attr.index;
|
||||
}
|
||||
|
||||
if ((attr_bits & attr_new) != 0) {
|
||||
std::cout << name_ << ": Attribute \"" << attr.name
|
||||
<< "\" overlap one or more index from another attribute."
|
||||
" Note that mat4 takes up 4 indices.";
|
||||
if (other_info) {
|
||||
std::cout << " While merging " << other_info->name_ << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
BLI_assert(0);
|
||||
}
|
||||
attr_bits |= attr_new;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gpu::shader
|
||||
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
#define GPU_SHADER_NAMED_INTERFACE_INFO(_interface, _inst_name) \
|
||||
StageInterfaceInfo *ptr_##_interface = new StageInterfaceInfo(#_interface, #_inst_name); \
|
||||
StageInterfaceInfo &_interface = *ptr_##_interface; \
|
||||
g_interfaces->add_new(#_interface, ptr_##_interface); \
|
||||
_interface
|
||||
|
||||
#define GPU_SHADER_INTERFACE_INFO(_interface) \
|
||||
StageInterfaceInfo *ptr_##_interface = new StageInterfaceInfo(#_interface); \
|
||||
StageInterfaceInfo &_interface = *ptr_##_interface; \
|
||||
g_interfaces->add_new(#_interface, ptr_##_interface); \
|
||||
_interface
|
||||
|
||||
#define GPU_SHADER_CREATE_INFO(_info) \
|
||||
ShaderCreateInfo *ptr_##_info = new ShaderCreateInfo(#_info); \
|
||||
ShaderCreateInfo &_info = *ptr_##_info; \
|
||||
g_create_infos->add_new(#_info, ptr_##_info); \
|
||||
_info
|
||||
|
||||
#define GPU_SHADER_NAMED_INTERFACE_END(_inst_name) ;
|
||||
#define GPU_SHADER_INTERFACE_END() ;
|
||||
#define GPU_SHADER_CREATE_END() ;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Disable optimization for this function with MSVC. It does not like the fact
|
||||
* shaders info are declared in the same function (same basic block or not does
|
||||
* not change anything).
|
||||
* Since it is just a function called to register shaders (once),
|
||||
* the fact it's optimized or not does not matter, it's not on any hot
|
||||
* code path. */
|
||||
# pragma optimize("", off)
|
||||
#endif
|
||||
|
||||
/* Split functions to avoid stack overflow on windows. */
|
||||
static void init_compositor_infos()
|
||||
{
|
||||
/* Declare, register and construct the infos. */
|
||||
#include "glsl_compositor_infos_list.hh"
|
||||
}
|
||||
|
||||
static void init_draw_infos()
|
||||
{
|
||||
/* Declare, register and construct the infos. */
|
||||
#include "glsl_draw_infos_list.hh"
|
||||
|
||||
if (GPU_stencil_clasify_buffer_workaround()) {
|
||||
/* WORKAROUND: Adding a dummy buffer that isn't used fixes a bug inside the Qualcomm driver. */
|
||||
eevee_deferred_tile_classify.storage_buf(
|
||||
12, Qualifier::read_write, "uint", "dummy_workaround_buf[]");
|
||||
}
|
||||
}
|
||||
|
||||
static void init_gpu_infos()
|
||||
{
|
||||
/* Declare, register and construct the infos. */
|
||||
#include "glsl_gpu_infos_list.hh"
|
||||
}
|
||||
|
||||
static void init_ocio_infos()
|
||||
{
|
||||
/* Declare, register and construct the infos. */
|
||||
#include "glsl_ocio_infos_list.hh"
|
||||
}
|
||||
|
||||
static void init_osd_infos()
|
||||
{
|
||||
/* Declare, register and construct the infos. */
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
# include "glsl_osd_infos_list.hh"
|
||||
#endif
|
||||
}
|
||||
|
||||
void gpu_shader_create_info_init()
|
||||
{
|
||||
g_create_infos = new CreateInfoDictionary();
|
||||
g_interfaces = new InterfaceDictionary();
|
||||
|
||||
init_compositor_infos();
|
||||
init_draw_infos();
|
||||
init_gpu_infos();
|
||||
init_ocio_infos();
|
||||
init_osd_infos();
|
||||
|
||||
for (ShaderCreateInfo *info : g_create_infos->values()) {
|
||||
info->is_generated_ = false;
|
||||
|
||||
info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_);
|
||||
info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_);
|
||||
info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_);
|
||||
info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_);
|
||||
|
||||
if (!info->compute_source_.is_empty()) {
|
||||
info->shared_variables_.extend(
|
||||
gpu_shader_dependency_get_shared_variables(info->compute_source_));
|
||||
}
|
||||
|
||||
#if GPU_SHADER_PRINTF_ENABLE
|
||||
const bool is_material_shader = StringRefNull(info->name_).startswith("eevee_surf_");
|
||||
if (flag_is_set(info->builtins_, BuiltinBits::USE_PRINTF) ||
|
||||
(gpu_shader_dependency_force_gpu_print_injection() && is_material_shader))
|
||||
{
|
||||
info->additional_info("gpu_print");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* Automatically amend the create info for ease of use of the debug feature. */
|
||||
if (flag_is_set(info->builtins_, BuiltinBits::USE_DEBUG_DRAW)) {
|
||||
info->additional_info("draw_debug_draw");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (ShaderCreateInfo *info : g_create_infos->values()) {
|
||||
info->finalize(true);
|
||||
}
|
||||
|
||||
/* TEST */
|
||||
// gpu_shader_create_info_compile(nullptr);
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
# pragma optimize("", on)
|
||||
#endif
|
||||
|
||||
void gpu_shader_create_info_exit()
|
||||
{
|
||||
for (auto *value : g_create_infos->values()) {
|
||||
delete value;
|
||||
}
|
||||
delete g_create_infos;
|
||||
|
||||
for (auto *value : g_interfaces->values()) {
|
||||
delete value;
|
||||
}
|
||||
delete g_interfaces;
|
||||
}
|
||||
|
||||
bool gpu_shader_create_info_compile_all(const char *name_starts_with_filter)
|
||||
{
|
||||
using namespace blender::gpu;
|
||||
int success = 0;
|
||||
int skipped_filter = 0;
|
||||
int skipped = 0;
|
||||
int total = 0;
|
||||
|
||||
Vector<AsyncCompilationHandle> handles;
|
||||
|
||||
for (ShaderCreateInfo *info : g_create_infos->values()) {
|
||||
info->finalize();
|
||||
if (info->do_static_compilation_) {
|
||||
if (name_starts_with_filter &&
|
||||
!StringRefNull(info->name_).startswith(StringRefNull(name_starts_with_filter)))
|
||||
{
|
||||
skipped_filter++;
|
||||
continue;
|
||||
}
|
||||
if ((info->metal_backend_only_ && GPU_backend_get_type() != GPU_BACKEND_METAL) ||
|
||||
(GPU_geometry_shader_support() == false && info->geometry_source_ != nullptr))
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
total++;
|
||||
|
||||
handles.append(
|
||||
GPU_shader_async_compilation(reinterpret_cast<const GPUShaderCreateInfo *>(info)));
|
||||
}
|
||||
}
|
||||
|
||||
GPU_shader_compiler_wait_for_all();
|
||||
|
||||
for (AsyncCompilationHandle handle : handles) {
|
||||
if (gpu::Shader *result = GPU_shader_async_compilation_finalize(handle)) {
|
||||
success++;
|
||||
#if 0 /* TODO(fclem): This is too verbose for now. Make it a cmake option. */
|
||||
/* Test if any resource is optimized out and print a warning if that's the case. */
|
||||
/* TODO(fclem): Limit this to OpenGL backend. */
|
||||
const ShaderInterface *interface = shader->interface;
|
||||
|
||||
Vector<ShaderCreateInfo::Resource> all_resources = info->resources_get_all_();
|
||||
|
||||
for (ShaderCreateInfo::Resource &res : all_resources) {
|
||||
StringRefNull name = "";
|
||||
const ShaderInput *input = nullptr;
|
||||
|
||||
switch (res.bind_type) {
|
||||
case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
|
||||
input = interface->ubo_get(res.slot);
|
||||
name = res.uniformbuf.name;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
|
||||
input = interface->ssbo_get(res.slot);
|
||||
name = res.storagebuf.name;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::SAMPLER:
|
||||
input = interface->texture_get(res.slot);
|
||||
name = res.sampler.name;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::IMAGE:
|
||||
input = interface->texture_get(res.slot);
|
||||
name = res.image.name;
|
||||
break;
|
||||
}
|
||||
|
||||
if (input == nullptr) {
|
||||
std::cerr << "Error: " << info->name_;
|
||||
std::cerr << ": Resource « " << name << " » not found in the shader interface\n";
|
||||
}
|
||||
else if (input->location == -1) {
|
||||
std::cerr << "Warning: " << info->name_;
|
||||
std::cerr << ": Resource « " << name << " » is optimized out\n";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
GPU_shader_free(result);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Shader Test compilation result: %d / %d passed", success, total);
|
||||
if (skipped_filter > 0) {
|
||||
printf(" (skipped %d when filtering)", skipped_filter);
|
||||
}
|
||||
if (skipped > 0) {
|
||||
printf(" (skipped %d for compatibility reasons)", skipped);
|
||||
}
|
||||
printf("\n");
|
||||
return success == total;
|
||||
}
|
||||
|
||||
const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name)
|
||||
{
|
||||
if (g_create_infos->contains(info_name) == false) {
|
||||
printf("Error: Cannot find shader create info named \"%s\"\n", info_name);
|
||||
return nullptr;
|
||||
}
|
||||
ShaderCreateInfo *info = g_create_infos->lookup(info_name);
|
||||
return reinterpret_cast<const GPUShaderCreateInfo *>(info);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1784
blender-5.2.0/source/blender/gpu/intern/gpu_shader_create_info.hh
Normal file
1784
blender-5.2.0/source/blender/gpu/intern/gpu_shader_create_info.hh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_primitive.hh"
|
||||
|
||||
#include "gpu_state_private.hh"
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
namespace blender::gpu::shader {
|
||||
|
||||
/**
|
||||
* \brief Description of a graphical pipeline to pre-compile during shader creation.
|
||||
*/
|
||||
struct PipelineState {
|
||||
Vector<SpecializationConstant::Value> specialization_constants_;
|
||||
/* Vertex input */
|
||||
GPUPrimType primitive_;
|
||||
/* Pre-fragment and Fragment stage. */
|
||||
GPUState state_ = {{GPU_WRITE_COLOR}};
|
||||
uint32_t viewport_count_;
|
||||
/* Attachment formats. */
|
||||
TextureTargetFormat depth_format_;
|
||||
TextureTargetFormat stencil_format_;
|
||||
Vector<TextureTargetFormat> color_formats_;
|
||||
|
||||
using Self = PipelineState;
|
||||
|
||||
Self &state(GPUWriteMask write_mask,
|
||||
GPUBlend blend,
|
||||
GPUFaceCullTest culling_test,
|
||||
GPUDepthTest depth_test,
|
||||
GPUStencilTest stencil_test,
|
||||
GPUStencilOp stencil_op,
|
||||
GPUProvokingVertex provoking_vert)
|
||||
{
|
||||
state_.write_mask = write_mask;
|
||||
state_.blend = blend;
|
||||
state_.culling_test = culling_test;
|
||||
state_.depth_test = depth_test;
|
||||
state_.stencil_test = stencil_test;
|
||||
state_.stencil_op = stencil_op;
|
||||
state_.provoking_vert = provoking_vert;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &logic_op_xor()
|
||||
{
|
||||
state_.logic_op_xor = 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &primitive(GPUPrimType primitive_type)
|
||||
{
|
||||
primitive_ = primitive_type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &viewports(uint32_t viewport_count)
|
||||
{
|
||||
viewport_count_ = viewport_count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &add_specialization_constant(SpecializationConstant::Value specialization_constant)
|
||||
{
|
||||
specialization_constants_.append(specialization_constant);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &depth_format(TextureTargetFormat depth_format)
|
||||
{
|
||||
BLI_assert(bool(to_format_flag(to_texture_format(depth_format)) & GPU_FORMAT_DEPTH));
|
||||
depth_format_ = depth_format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &stencil_format(TextureTargetFormat stencil_format)
|
||||
{
|
||||
BLI_assert(bool(to_format_flag(to_texture_format(stencil_format)) & GPU_FORMAT_STENCIL));
|
||||
stencil_format_ = stencil_format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Self &color_format(TextureTargetFormat color_format)
|
||||
{
|
||||
BLI_assert(bool(to_format_flag(to_texture_format(color_format)) &
|
||||
(GPU_FORMAT_STENCIL | GPU_FORMAT_DEPTH)) == false);
|
||||
color_formats_.append(color_format);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::shader
|
||||
@@ -0,0 +1,31 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Descriptor type used to define shader structure, resources and interfaces.
|
||||
*
|
||||
* Some rule of thumb:
|
||||
* - Do not include anything else than this file in each descriptor file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void gpu_shader_create_info_init();
|
||||
void gpu_shader_create_info_exit();
|
||||
|
||||
bool gpu_shader_create_info_compile_all(const char *name_starts_with_filter);
|
||||
|
||||
/**
|
||||
* Runtime created shader information is not registered in the dictionary
|
||||
* and cannot be searched.
|
||||
*/
|
||||
const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,417 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_bit_vector.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "shader_tool/lexit/lexit.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Streaming Dead Code Eliminator.
|
||||
* \{ */
|
||||
|
||||
/* Token stream that parses (some) symbols definitions, build a graph of usage and then prune
|
||||
* unused definitions for a given set of entry point functions. */
|
||||
struct PruningStream {
|
||||
using Token = lexit::Token;
|
||||
using TokenType = lexit::TokenType;
|
||||
using TokenAtom = lexit::TokenAtom;
|
||||
|
||||
private:
|
||||
/* Data to output. Think of it as a lazy string builder. */
|
||||
Vector<StringRef> stream;
|
||||
/* Total output length. */
|
||||
size_t total_length = 0;
|
||||
/* Ranges of character in the stream to remove when constructing the result. */
|
||||
Vector<std::pair<int32_t, int32_t>> removals;
|
||||
|
||||
/* Simple circular buffer to access last few token data. */
|
||||
class TokenHistoryBuffer {
|
||||
private:
|
||||
std::array<Token, 2> buffer;
|
||||
int head = 0;
|
||||
|
||||
public:
|
||||
TokenHistoryBuffer(Token invalid) : buffer({invalid, invalid}) {}
|
||||
|
||||
void push(Token tok)
|
||||
{
|
||||
buffer[head] = tok;
|
||||
head = (head + 1) % buffer.size();
|
||||
}
|
||||
|
||||
/* Access elements from newest (0) to oldest (count-1). */
|
||||
const Token &operator[](int index) const
|
||||
{
|
||||
BLI_assert(index < buffer.size());
|
||||
int actual_index = (head + buffer.size() - index - 1) % buffer.size();
|
||||
return buffer[actual_index];
|
||||
}
|
||||
} token_history;
|
||||
|
||||
struct FunctionDeclaration {
|
||||
Token type, name, end;
|
||||
};
|
||||
|
||||
struct FunctionGraph {
|
||||
Vector<FunctionDeclaration> declarations;
|
||||
/* Function call (from, to). */
|
||||
Vector<std::pair<TokenAtom, TokenAtom>> edges;
|
||||
} graph;
|
||||
|
||||
TokenAtom current_fn = 0;
|
||||
|
||||
/* State of the parser. Some section have DCE turned off because of unsupported syntax. */
|
||||
bool enabled_ = true;
|
||||
|
||||
int stack_depth = 0;
|
||||
|
||||
const TokenAtom return_atom;
|
||||
const TokenAtom thread_atom;
|
||||
const TokenAtom device_atom;
|
||||
|
||||
/* Bit vector allowing to filter atoms that designate builtin functions or constructors that we
|
||||
* should avoid creating graph for. */
|
||||
bits::BitVector<> builtin_atoms;
|
||||
|
||||
public:
|
||||
PruningStream(Token invalid_tok,
|
||||
TokenAtom return_atom,
|
||||
TokenAtom thread_atom,
|
||||
TokenAtom device_atom,
|
||||
Span<TokenAtom> builtin_atoms)
|
||||
: token_history(invalid_tok),
|
||||
return_atom(return_atom),
|
||||
thread_atom(thread_atom),
|
||||
device_atom(device_atom),
|
||||
/* Reserve enough for all atoms, even generated ones during macro expansions. */
|
||||
builtin_atoms(0x10000)
|
||||
{
|
||||
for (auto atom : builtin_atoms) {
|
||||
/* Atom values can only be separated by at least 2 values. */
|
||||
this->builtin_atoms[atom / 2].set(true);
|
||||
}
|
||||
}
|
||||
|
||||
void set_enabled_parsing(bool value)
|
||||
{
|
||||
enabled_ = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if str is a contiguous continuation of the last inserted string ref.
|
||||
* If yes, merge it; else append str to the stream.
|
||||
*/
|
||||
PruningStream &operator<<(StringRef str)
|
||||
{
|
||||
if (str.is_empty()) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (!stream.is_empty()) {
|
||||
StringRef &last = stream.last();
|
||||
/* Check if the memory addresses are contiguous */
|
||||
if (last.data() + last.size() == str.data()) {
|
||||
last = StringRef(last.data(), last.size() + str.size());
|
||||
}
|
||||
else {
|
||||
stream.append(str);
|
||||
}
|
||||
}
|
||||
else {
|
||||
stream.append(str);
|
||||
}
|
||||
|
||||
total_length += str.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
BLI_NOINLINE void parse_token(const Token tok)
|
||||
{
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
parse_token_impl(set_start_char(tok));
|
||||
}
|
||||
|
||||
BLI_NOINLINE void parse_token_range(const Token start, const Token end)
|
||||
{
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
const char *start_char = start.str_with_whitespace().data();
|
||||
|
||||
Token tok = start;
|
||||
|
||||
/* Process first 2 token to avoid branching in the loop. */
|
||||
parse_token_impl(set_start_char(tok));
|
||||
if (tok == end) {
|
||||
return;
|
||||
}
|
||||
tok = tok.next();
|
||||
parse_token_impl(set_start_char(tok, start_char));
|
||||
if (tok == end) {
|
||||
return;
|
||||
}
|
||||
tok = tok.next();
|
||||
|
||||
blender::IndexRange range(int(end) - int(tok) + 1);
|
||||
Span<TokenType> types(&tok.type(), range.size());
|
||||
|
||||
for (int i : range) {
|
||||
switch (types[i]) {
|
||||
case TokenType::ParOpen:
|
||||
/* Fill the needed history JIT. */
|
||||
token_history.push(set_start_char(tok.next(i).prev(2), start_char));
|
||||
token_history.push(set_start_char(tok.next(i).prev(1), start_char));
|
||||
process_function();
|
||||
break;
|
||||
case TokenType::BracketOpen:
|
||||
stack_depth += (current_fn != 0);
|
||||
break;
|
||||
case TokenType::BracketClose:
|
||||
stack_depth -= (current_fn != 0);
|
||||
ATTR_FALLTHROUGH;
|
||||
case TokenType::SemiColon:
|
||||
/* Finding a semicolon in global scope after a function signature means that this is
|
||||
* a forward declaration. Step out of function in this case. */
|
||||
register_function_end(set_start_char(tok.next(i), start_char));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register last two tokens. */
|
||||
token_history.push(set_start_char(end.prev(), start_char));
|
||||
token_history.push(set_start_char(end, start_char));
|
||||
}
|
||||
|
||||
std::string str() const
|
||||
{
|
||||
if (total_length == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.reserve(total_length);
|
||||
|
||||
size_t global_offset = 0;
|
||||
const auto *rem_it = removals.begin();
|
||||
|
||||
for (const auto &segment : stream) {
|
||||
size_t seg_idx = 0;
|
||||
/* Process the current segment until we reach its end */
|
||||
while (seg_idx < segment.size()) {
|
||||
size_t current_global_pos = global_offset + seg_idx;
|
||||
/* If no active removals remain, copy the rest of the segment. */
|
||||
if (rem_it == removals.end()) {
|
||||
result.append(segment, seg_idx, std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
auto [r_start, r_end] = *rem_it;
|
||||
/* Safety check: ensure we haven't somehow passed the removal (if unsorted) */
|
||||
BLI_assert(current_global_pos < r_end);
|
||||
|
||||
if (current_global_pos < r_start) {
|
||||
/* We are before the removal starts.
|
||||
* Copy until the segment ends OR the removal starts */
|
||||
size_t len = std::min(size_t(segment.size()) - seg_idx, r_start - current_global_pos);
|
||||
result.append(segment, seg_idx, len);
|
||||
seg_idx += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We are inside the removal range.
|
||||
* Process until the segment ends OR the removal ends */
|
||||
size_t len = std::min(size_t(segment.size()) - seg_idx, r_end - current_global_pos);
|
||||
/* Replace the removed chunk by newlines. */
|
||||
std::string_view chunk(segment.data() + seg_idx, len);
|
||||
size_t newlines = std::count(chunk.begin(), chunk.end(), '\n');
|
||||
result.append(newlines, '\n');
|
||||
seg_idx += len;
|
||||
/* If we reached the end of this removal range, move to the next one */
|
||||
if (global_offset + seg_idx >= r_end) {
|
||||
++rem_it;
|
||||
}
|
||||
}
|
||||
/* Update global offset before moving to next segment */
|
||||
global_offset += segment.size();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void optimize(const Span<TokenAtom> entry_points)
|
||||
{
|
||||
prune_unused_functions(entry_points);
|
||||
}
|
||||
|
||||
private:
|
||||
Token set_start_char(Token tok, const char *start_offset = nullptr)
|
||||
{
|
||||
int offset = start_offset ? tok.str_with_whitespace().data() - start_offset : 0;
|
||||
tok.flag = int32_t(this->total_length + offset);
|
||||
return tok;
|
||||
}
|
||||
|
||||
BLI_INLINE_METHOD void parse_token_impl(const Token tok)
|
||||
{
|
||||
switch (tok.type()) {
|
||||
case TokenType::ParOpen:
|
||||
process_function();
|
||||
break;
|
||||
case TokenType::BracketOpen:
|
||||
stack_depth += (current_fn != 0);
|
||||
break;
|
||||
case TokenType::BracketClose:
|
||||
stack_depth -= (current_fn != 0);
|
||||
ATTR_FALLTHROUGH;
|
||||
case TokenType::SemiColon:
|
||||
/* Finding a semicolon in global scope after a function signature means that this is
|
||||
* a forward declaration. Step out of function in this case. */
|
||||
register_function_end(tok);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
token_history.push(tok);
|
||||
}
|
||||
|
||||
void process_function()
|
||||
{
|
||||
Token name_tok = token_history[0];
|
||||
if (name_tok.type() != TokenType::Word) {
|
||||
return;
|
||||
}
|
||||
|
||||
Token type_tok = token_history[1];
|
||||
|
||||
/* Filter MSL & GLSL specific identifiers that could have confused the parser. */
|
||||
if (type_tok.atom() == thread_atom || type_tok.atom() == device_atom) {
|
||||
return;
|
||||
}
|
||||
if (builtin_atoms[name_tok.atom() / 2]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_tok.type() == TokenType::Word && type_tok.atom() != return_atom) {
|
||||
register_function_declaration(type_tok, name_tok);
|
||||
}
|
||||
else if (current_fn != 0) {
|
||||
register_function_call(name_tok);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE_METHOD void register_function_end(Token tok)
|
||||
{
|
||||
if (stack_depth == 0 && current_fn != 0) {
|
||||
graph.declarations.last().end = tok;
|
||||
current_fn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register function declaration at this token position.
|
||||
* Associate ID with the token string if first encountering the symbol.
|
||||
* Does not differentiate overloads. */
|
||||
void register_function_declaration(Token type_tok, Token name_tok)
|
||||
{
|
||||
BLI_assert_msg(graph.declarations.is_empty() ||
|
||||
graph.declarations.last().name.flag != graph.declarations.last().end.flag,
|
||||
"Missing call to register_function_end");
|
||||
|
||||
graph.declarations.append(FunctionDeclaration{type_tok, name_tok, name_tok});
|
||||
current_fn = name_tok.atom();
|
||||
}
|
||||
|
||||
/* Register a function call made inside the body of a function by creating an edge inside the
|
||||
* graph. */
|
||||
void register_function_call(Token name_tok)
|
||||
{
|
||||
graph.edges.append_as(current_fn, name_tok.atom());
|
||||
}
|
||||
|
||||
Map<TokenAtom, Vector<TokenAtom>> build_adjacency()
|
||||
{
|
||||
Map<TokenAtom, Vector<TokenAtom>> adj;
|
||||
adj.reserve(graph.declarations.size());
|
||||
for (const auto &[from, to] : graph.edges) {
|
||||
adj.lookup_or_add_default(from).append(to);
|
||||
}
|
||||
return adj;
|
||||
}
|
||||
|
||||
Set<TokenAtom> compute_used_functions(Span<TokenAtom> &roots)
|
||||
{
|
||||
Set<TokenAtom> used;
|
||||
used.reserve(graph.declarations.size());
|
||||
|
||||
auto adj = build_adjacency();
|
||||
|
||||
std::vector<TokenAtom> stack;
|
||||
stack.reserve(64);
|
||||
|
||||
for (TokenAtom root : roots) {
|
||||
if (used.add(root)) {
|
||||
stack.push_back(root);
|
||||
}
|
||||
|
||||
while (!stack.empty()) {
|
||||
TokenAtom f = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
const auto *calls = adj.lookup_ptr(f);
|
||||
if (calls == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (TokenAtom callee : *calls) {
|
||||
if (used.add(callee)) {
|
||||
stack.push_back(callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
void prune_unused_functions(Span<TokenAtom> entry_points)
|
||||
{
|
||||
Set<TokenAtom> used = compute_used_functions(entry_points);
|
||||
|
||||
for (auto [type_tok, name_tok, end_tok] : graph.declarations) {
|
||||
if (used.contains(name_tok.atom())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name_tok.flag == end_tok.flag) {
|
||||
removals.clear();
|
||||
BLI_assert("Bug in parser, function name not detected");
|
||||
break;
|
||||
}
|
||||
|
||||
remove_range(type_tok.flag, end_tok.flag + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove a range of character from the final string but keeping spaces. */
|
||||
void remove_range(int start_char, int end_char)
|
||||
{
|
||||
removals.append_as(start_char, end_char);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
696
blender-5.2.0/source/blender/gpu/intern/gpu_shader_dependency.cc
Normal file
696
blender-5.2.0/source/blender/gpu/intern/gpu_shader_dependency.cc
Normal file
@@ -0,0 +1,696 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Shader source dependency builder that make possible to support #include directive inside the
|
||||
* shader files.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <fmt/format.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "gpu_capabilities_private.hh"
|
||||
#include "gpu_material_library.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_dependency_private.hh"
|
||||
#include "gpu_shader_metadata_private.hh"
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
# include "opensubdiv_capi_type.hh"
|
||||
# include "opensubdiv_evaluator_capi.hh"
|
||||
#endif
|
||||
|
||||
#include "../shader_tool/metadata.hh"
|
||||
#include "../shader_tool/processor.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern "C" {
|
||||
#define SHADER_SOURCE(filename_underscore, filename, filepath) \
|
||||
extern char datatoc_##filename_underscore[];
|
||||
#include "glsl_compositor_source_list.h"
|
||||
#include "glsl_draw_source_list.h"
|
||||
#include "glsl_gpu_source_list.h"
|
||||
#include "glsl_ocio_source_list.h"
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
# include "glsl_osd_source_list.h"
|
||||
#endif
|
||||
#undef SHADER_SOURCE
|
||||
}
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.shader.deps"};
|
||||
|
||||
namespace gpu::shader {
|
||||
|
||||
static bool g_shader_use_printf = false;
|
||||
|
||||
shader::BuiltinBits convert_builtin_bit(shader::metadata::Builtin builtin)
|
||||
{
|
||||
using namespace blender::gpu::shader;
|
||||
using namespace blender::gpu::shader::metadata;
|
||||
switch (builtin) {
|
||||
case Builtin::FragCoord:
|
||||
return BuiltinBits::FRAG_COORD;
|
||||
case Builtin::FragStencilRef:
|
||||
return BuiltinBits::STENCIL_REF;
|
||||
case Builtin::FrontFacing:
|
||||
return BuiltinBits::FRONT_FACING;
|
||||
case Builtin::GlobalInvocationID:
|
||||
return BuiltinBits::GLOBAL_INVOCATION_ID;
|
||||
case Builtin::InstanceIndex:
|
||||
case Builtin::BaseInstance:
|
||||
case Builtin::InstanceID:
|
||||
return BuiltinBits::INSTANCE_ID;
|
||||
case Builtin::LocalInvocationID:
|
||||
return BuiltinBits::LOCAL_INVOCATION_ID;
|
||||
case Builtin::LocalInvocationIndex:
|
||||
return BuiltinBits::LOCAL_INVOCATION_INDEX;
|
||||
case Builtin::NumWorkGroup:
|
||||
return BuiltinBits::NUM_WORK_GROUP;
|
||||
case Builtin::PointCoord:
|
||||
return BuiltinBits::POINT_COORD;
|
||||
case Builtin::PointSize:
|
||||
return BuiltinBits::POINT_SIZE;
|
||||
case Builtin::PrimitiveID:
|
||||
return BuiltinBits::PRIMITIVE_ID;
|
||||
case Builtin::VertexID:
|
||||
return BuiltinBits::VERTEX_ID;
|
||||
case Builtin::WorkGroupID:
|
||||
return BuiltinBits::WORK_GROUP_ID;
|
||||
case Builtin::WorkGroupSize:
|
||||
return BuiltinBits::WORK_GROUP_SIZE;
|
||||
case Builtin::ClipDistance:
|
||||
return BuiltinBits::CLIP_DISTANCES;
|
||||
case Builtin::drw_debug:
|
||||
#ifndef NDEBUG
|
||||
return BuiltinBits::USE_DEBUG_DRAW;
|
||||
#else
|
||||
return BuiltinBits::NONE;
|
||||
#endif
|
||||
case Builtin::assert:
|
||||
case Builtin::printf:
|
||||
#if GPU_SHADER_PRINTF_ENABLE
|
||||
g_shader_use_printf = true;
|
||||
return BuiltinBits::USE_PRINTF;
|
||||
#else
|
||||
return BuiltinBits::NONE;
|
||||
#endif
|
||||
case Builtin::runtime_generated:
|
||||
return BuiltinBits::RUNTIME_GENERATED;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return BuiltinBits::NONE;
|
||||
}
|
||||
|
||||
} // namespace gpu::shader
|
||||
|
||||
namespace gpu {
|
||||
|
||||
using GPUPrintFormatMap = Map<uint32_t, shader::PrintfFormat>;
|
||||
using GPUSourceDictionary = Map<StringRef, GPUSource *>;
|
||||
using GPUFunctionDictionary = Map<StringRef, GPUFunction *>;
|
||||
|
||||
struct GPUSource {
|
||||
StringRefNull fullpath;
|
||||
StringRefNull filename;
|
||||
StringRefNull source;
|
||||
std::string patched_source;
|
||||
Vector<StringRef> dependencies_names;
|
||||
Vector<GPUSource *> dependencies;
|
||||
bool dependencies_init = false;
|
||||
shader::BuiltinBits builtins = shader::BuiltinBits::NONE;
|
||||
/* True if this file content is supposed to be generated at runtime. */
|
||||
bool generated = false;
|
||||
|
||||
Vector<shader::ShaderCreateInfo::SharedVariable, 0> shared_variables;
|
||||
|
||||
/* NOTE: The next few functions are needed to keep isolation of the preprocessor.
|
||||
* Eventually, this should be revisited and the preprocessor should output
|
||||
* GPU structures. */
|
||||
|
||||
GPUFunctionQual convert_qualifier(shader::metadata::Qualifier qualifier)
|
||||
{
|
||||
using namespace blender::gpu::shader;
|
||||
switch (qualifier) {
|
||||
case metadata::Qualifier::in:
|
||||
return FUNCTION_QUAL_IN;
|
||||
case metadata::Qualifier::out:
|
||||
return FUNCTION_QUAL_OUT;
|
||||
case metadata::Qualifier::inout:
|
||||
return FUNCTION_QUAL_INOUT;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return FUNCTION_QUAL_IN;
|
||||
}
|
||||
|
||||
GPUType convert_type(shader::metadata::Type type)
|
||||
{
|
||||
using namespace blender::gpu::shader;
|
||||
switch (type) {
|
||||
case metadata::Type::float1:
|
||||
return GPU_FLOAT;
|
||||
case metadata::Type::float2:
|
||||
return GPU_VEC2;
|
||||
case metadata::Type::float3:
|
||||
return GPU_VEC3;
|
||||
case metadata::Type::float4:
|
||||
return GPU_VEC4;
|
||||
case metadata::Type::float3x3:
|
||||
return GPU_MAT3;
|
||||
case metadata::Type::float4x4:
|
||||
return GPU_MAT4;
|
||||
case metadata::Type::sampler1DArray:
|
||||
return GPU_TEX1D_ARRAY;
|
||||
case metadata::Type::sampler2DArray:
|
||||
return GPU_TEX2D_ARRAY;
|
||||
case metadata::Type::sampler2D:
|
||||
return GPU_TEX2D;
|
||||
case metadata::Type::sampler3D:
|
||||
return GPU_TEX3D;
|
||||
case metadata::Type::Closure:
|
||||
return GPU_CLOSURE;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return GPU_NONE;
|
||||
}
|
||||
|
||||
GPUSource(
|
||||
const char *path,
|
||||
const char *file,
|
||||
const char *datatoc,
|
||||
GPUFunctionDictionary *g_functions,
|
||||
GPUPrintFormatMap *g_formats,
|
||||
std::function<void(GPUSource &, GPUFunctionDictionary *, GPUPrintFormatMap *)> metadata_fn)
|
||||
: fullpath(path), filename(file), source(datatoc)
|
||||
{
|
||||
BLI_assert_msg(source.find("//") == std::string::npos &&
|
||||
source.find("/*") == std::string::npos,
|
||||
"Input source should have no comments.");
|
||||
metadata_fn(*this, g_functions, g_formats);
|
||||
};
|
||||
|
||||
void add_builtin(shader::metadata::Builtin builtin)
|
||||
{
|
||||
builtins |= shader::convert_builtin_bit(builtin);
|
||||
}
|
||||
|
||||
void add_dependency(StringRef line)
|
||||
{
|
||||
dependencies_names.append(line);
|
||||
}
|
||||
|
||||
void add_shared_variable(const shader::Type type, const StringRefNull name)
|
||||
{
|
||||
shared_variables.append({type, name});
|
||||
}
|
||||
|
||||
void add_printf_format(uint32_t format_hash, std::string format, GPUPrintFormatMap *format_map)
|
||||
{
|
||||
/* TODO(fclem): Move this to gpu log. */
|
||||
if (format_map->contains(format_hash)) {
|
||||
if (format_map->lookup(format_hash).format_str != format) {
|
||||
print_error(format, 0, "printf format hash collision.");
|
||||
}
|
||||
else {
|
||||
/* The format map already have the same format. */
|
||||
}
|
||||
}
|
||||
else {
|
||||
shader::PrintfFormat fmt;
|
||||
/* Save for hash collision comparison. */
|
||||
fmt.format_str = format;
|
||||
|
||||
/* Escape characters replacement. Do the most common ones. */
|
||||
format = std::regex_replace(format, std::regex(R"(\\n)"), "\n");
|
||||
format = std::regex_replace(format, std::regex(R"(\\v)"), "\v");
|
||||
format = std::regex_replace(format, std::regex(R"(\\t)"), "\t");
|
||||
format = std::regex_replace(format, std::regex(R"(\\')"), "\'");
|
||||
format = std::regex_replace(format, std::regex(R"(\\")"), "\"");
|
||||
format = std::regex_replace(format, std::regex(R"(\\\\)"), "\\");
|
||||
|
||||
shader::PrintfFormat::Block::ArgumentType type =
|
||||
shader::PrintfFormat::Block::ArgumentType::NONE;
|
||||
int64_t start = 0, end = 0, cursor = -1;
|
||||
while ((end = format.find_first_of('%', cursor + 1)) != -1) {
|
||||
if (end - start > 0) {
|
||||
/* Add the previous block without the newly found % character. */
|
||||
fmt.format_blocks.append({type, format.substr(start, end - start)});
|
||||
}
|
||||
/* Format type of the next block. */
|
||||
/* TODO(fclem): This doesn't support advance formats like `%3.2f`. */
|
||||
switch (format[end + 1]) {
|
||||
case 's':
|
||||
type = shader::PrintfFormat::Block::ArgumentType::STRING;
|
||||
break;
|
||||
case 'x':
|
||||
case 'u':
|
||||
type = shader::PrintfFormat::Block::ArgumentType::UINT;
|
||||
break;
|
||||
case 'd':
|
||||
type = shader::PrintfFormat::Block::ArgumentType::INT;
|
||||
break;
|
||||
case 'f':
|
||||
type = shader::PrintfFormat::Block::ArgumentType::FLOAT;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(0, "Printing format unsupported");
|
||||
break;
|
||||
}
|
||||
/* Start of the next block. */
|
||||
start = end;
|
||||
cursor = end;
|
||||
}
|
||||
fmt.format_blocks.append({type, format.substr(start, format.size() - start)});
|
||||
|
||||
format_map->add(format_hash, fmt);
|
||||
}
|
||||
}
|
||||
|
||||
void add_function(StringRefNull name,
|
||||
Span<shader::metadata::ArgumentFormat> arguments,
|
||||
GPUFunctionDictionary *g_functions)
|
||||
{
|
||||
GPUFunction *func = MEM_new<GPUFunction>(__func__);
|
||||
name.copy_utf8_truncated(func->name, sizeof(func->name));
|
||||
func->source = reinterpret_cast<void *>(this);
|
||||
func->totparam = 0;
|
||||
for (auto arg : arguments) {
|
||||
if (func->totparam >= ARRAY_SIZE(func->paramtype)) {
|
||||
print_error(source, source.find(name), "Too many parameters in function");
|
||||
break;
|
||||
}
|
||||
func->paramqual[func->totparam] = convert_qualifier(arg.qualifier);
|
||||
func->paramtype[func->totparam] = convert_type(arg.type);
|
||||
func->totparam++;
|
||||
}
|
||||
|
||||
bool insert = g_functions->add(func->name, func);
|
||||
/* NOTE: We allow overloading non void function, but only if the function comes from the
|
||||
* same file. Otherwise the dependency system breaks. */
|
||||
if (!insert) {
|
||||
GPUSource *other_source = reinterpret_cast<GPUSource *>(g_functions->lookup(name)->source);
|
||||
if (other_source != this) {
|
||||
const char *msg = "Function redefinition or overload in two different files ...";
|
||||
print_error(source, source.find(name), msg);
|
||||
print_error(other_source->source,
|
||||
other_source->source.find(name),
|
||||
"... previous definition was here");
|
||||
}
|
||||
else {
|
||||
/* Non-void function overload. */
|
||||
MEM_delete(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_error(const StringRef &input, int64_t offset, const StringRef message)
|
||||
{
|
||||
StringRef sub = input.substr(0, offset);
|
||||
int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1;
|
||||
int64_t line_end = input.find("\n", offset);
|
||||
int64_t line_start = input.rfind("\n", offset) + 1;
|
||||
int64_t char_number = offset - line_start + 1;
|
||||
|
||||
/* TODO Use clog. */
|
||||
|
||||
std::cerr << fullpath << ":" << line_number << ":" << char_number;
|
||||
|
||||
std::cerr << " error: " << message << "\n";
|
||||
std::cerr << std::setw(5) << line_number << " | "
|
||||
<< input.substr(line_start, line_end - line_start) << "\n";
|
||||
std::cerr << " | ";
|
||||
for (int64_t i = 0; i < char_number - 1; i++) {
|
||||
std::cerr << " ";
|
||||
}
|
||||
std::cerr << "^\n";
|
||||
}
|
||||
|
||||
/* Return 1 one error. */
|
||||
int init_dependencies(const GPUSourceDictionary &dict)
|
||||
{
|
||||
if (this->dependencies_init) {
|
||||
return 0;
|
||||
}
|
||||
this->dependencies_init = true;
|
||||
|
||||
using namespace shader;
|
||||
/* Auto dependency injection for debug capabilities. */
|
||||
if (flag_is_set(builtins, BuiltinBits::USE_PRINTF)) {
|
||||
dependencies.append_non_duplicates(dict.lookup("gpu_shader_print_lib.glsl"));
|
||||
}
|
||||
if (flag_is_set(builtins, BuiltinBits::USE_DEBUG_DRAW)) {
|
||||
dependencies.append_non_duplicates(dict.lookup("draw_debug_draw_lib.glsl"));
|
||||
}
|
||||
|
||||
for (auto dependency_name : dependencies_names) {
|
||||
GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr);
|
||||
if (dependency_source == nullptr) {
|
||||
std::string error = std::string("Dependency not found : ") + dependency_name;
|
||||
print_error(source, 0, error.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Recursive. */
|
||||
int result = dependency_source->init_dependencies(dict);
|
||||
if (result != 0) {
|
||||
return 1;
|
||||
}
|
||||
dependencies.append_non_duplicates(dependency_source);
|
||||
|
||||
this->shared_variables.extend(dependency_source->shared_variables);
|
||||
}
|
||||
dependencies_names.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void source_get(Vector<StringRefNull> &result,
|
||||
const shader::GeneratedSourceList &generated_sources,
|
||||
const GPUSourceDictionary &dict,
|
||||
const GPUSource &from) const
|
||||
{
|
||||
#define CLOG_FILE_INCLUDE(_from, _include) \
|
||||
if (CLOG_CHECK(&LOG, CLG_LEVEL_TRACE) && \
|
||||
(from).filename.c_str() != (_include).filename.c_str()) { \
|
||||
const char *from_filename = (_from).filename.c_str(); \
|
||||
const char *include_filename = (_include).filename.c_str(); \
|
||||
const int from_size = int((_from).source.size()); \
|
||||
const int include_size = int((_include).source.size()); \
|
||||
std::string link = fmt::format( \
|
||||
"{}_{} --> {}_{}\n", from_filename, from_size, include_filename, include_size); \
|
||||
std::string style = fmt::format("style {}_{} fill:#{:x}{:x}0\n", \
|
||||
include_filename, \
|
||||
include_size, \
|
||||
min_uu(15, include_size / 1000), \
|
||||
15 - min_uu(15, include_size / 1000)); \
|
||||
CLG_log_raw(LOG.type, link.c_str()); \
|
||||
CLG_log_raw(LOG.type, style.c_str()); \
|
||||
}
|
||||
|
||||
/* Check if this file was already included. */
|
||||
for (const StringRefNull &source_content : result) {
|
||||
/* Yes, compare pointer instead of string for speed.
|
||||
* Each source is guaranteed to be unique and non-moving during the building process. */
|
||||
if (source_content.c_str() == this->source.c_str()) {
|
||||
/* Already included. */
|
||||
CLOG_FILE_INCLUDE(from, *this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag_is_set(this->builtins, shader::BuiltinBits::RUNTIME_GENERATED)) {
|
||||
for (const auto &dependency : this->dependencies) {
|
||||
/* WATCH: Recursive. */
|
||||
dependency->source_get(result, generated_sources, dict, *this);
|
||||
}
|
||||
CLOG_FILE_INCLUDE(from, *this);
|
||||
result.append(this->source);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Linear lookup since we won't have more than a few per shaders.
|
||||
* Also avoid the complexity of a Map in info creation. */
|
||||
for (const shader::GeneratedSource &generated_src : generated_sources) {
|
||||
if (generated_src.filename == this->filename) {
|
||||
/* Include dependencies before the generated file. */
|
||||
for (const auto &dependency_name : generated_src.dependencies) {
|
||||
BLI_assert_msg(dependency_name != this->filename, "Recursive include");
|
||||
|
||||
GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr);
|
||||
if (dependency_source == nullptr) {
|
||||
/* Will certainly fail compilation. But avoid crashing the application. */
|
||||
std::cerr << "Generated dependency not found : " + dependency_name << std::endl;
|
||||
return;
|
||||
}
|
||||
/* WATCH: Recursive. */
|
||||
dependency_source->source_get(result, generated_sources, dict, *this);
|
||||
}
|
||||
CLOG_FILE_INCLUDE(from, *this);
|
||||
result.append(generated_src.content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "warn: Generated source not provided. Using fallback for : " << this->filename
|
||||
<< std::endl;
|
||||
/* Dependencies for generated sources are not folded on startup.
|
||||
* This allows for different set of dependencies at runtime. */
|
||||
for (const auto &dependency : this->dependencies) {
|
||||
/* WATCH: Recursive. */
|
||||
dependency->source_get(result, generated_sources, dict, *this);
|
||||
}
|
||||
CLOG_FILE_INCLUDE(from, *this);
|
||||
result.append(this->source);
|
||||
}
|
||||
|
||||
/* Returns the final string with all includes done. */
|
||||
void build(Vector<StringRefNull> &result,
|
||||
const shader::GeneratedSourceList &generated_sources,
|
||||
const GPUSourceDictionary &dict) const
|
||||
{
|
||||
source_get(result, generated_sources, dict, *this);
|
||||
}
|
||||
|
||||
shader::BuiltinBits builtins_get() const
|
||||
{
|
||||
shader::BuiltinBits out_builtins = builtins;
|
||||
for (auto *dep : dependencies) {
|
||||
out_builtins |= dep->builtins_get();
|
||||
}
|
||||
return out_builtins;
|
||||
}
|
||||
|
||||
bool is_from_material_library() const
|
||||
{
|
||||
return (filename.startswith("gpu_shader_material_") ||
|
||||
filename.startswith("gpu_shader_common_") ||
|
||||
filename.startswith("gpu_shader_compositor_")) &&
|
||||
filename.endswith(".glsl");
|
||||
}
|
||||
};
|
||||
|
||||
namespace shader {
|
||||
|
||||
#include "glsl_compositor_metadata_list.hh"
|
||||
#include "glsl_draw_metadata_list.hh"
|
||||
#include "glsl_gpu_metadata_list.hh"
|
||||
#include "glsl_ocio_metadata_list.hh"
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
# include "glsl_osd_metadata_list.hh"
|
||||
#endif
|
||||
|
||||
} // namespace shader
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
static GPUPrintFormatMap *g_formats = nullptr;
|
||||
static GPUSourceDictionary *g_sources = nullptr;
|
||||
static GPUFunctionDictionary *g_functions = nullptr;
|
||||
static bool force_printf_injection = false;
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
/* Using a global string to avoid dealing with memory allocation/ownership. */
|
||||
static std::string osd_patch_basis;
|
||||
#endif
|
||||
|
||||
void gpu_shader_dependency_init()
|
||||
{
|
||||
g_formats = new GPUPrintFormatMap();
|
||||
g_sources = new GPUSourceDictionary();
|
||||
g_functions = new GPUFunctionDictionary();
|
||||
|
||||
#define SHADER_SOURCE(filename_underscore, filename, filepath) \
|
||||
g_sources->add_new(filename, \
|
||||
new GPUSource(filepath, \
|
||||
filename, \
|
||||
datatoc_##filename_underscore, \
|
||||
g_functions, \
|
||||
g_formats, \
|
||||
gpu::shader::metadata_##filename_underscore));
|
||||
|
||||
#include "glsl_compositor_source_list.h"
|
||||
#include "glsl_draw_source_list.h"
|
||||
#include "glsl_gpu_source_list.h"
|
||||
#include "glsl_ocio_source_list.h"
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
# include "glsl_osd_source_list.h"
|
||||
#endif
|
||||
#undef SHADER_SOURCE
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
osd_patch_basis = openSubdiv_getGLSLPatchBasisSource();
|
||||
osd_patch_basis = shader::SourceProcessor(
|
||||
osd_patch_basis, "osd_patch_basis.glsl", gpu::shader::Language::GLSL)
|
||||
.remove_comments();
|
||||
auto source_ptr_opt = g_sources->pop_try("osd_patch_basis.glsl");
|
||||
if (source_ptr_opt) {
|
||||
delete source_ptr_opt.value();
|
||||
}
|
||||
g_sources->add_new(
|
||||
"osd_patch_basis.glsl",
|
||||
new GPUSource("osd_patch_basis.glsl",
|
||||
"osd_patch_basis.glsl",
|
||||
(osd_patch_basis).c_str(),
|
||||
g_functions,
|
||||
g_formats,
|
||||
[](GPUSource &, GPUFunctionDictionary *, GPUPrintFormatMap *) {}));
|
||||
#endif
|
||||
|
||||
int errors = 0;
|
||||
for (auto *value : g_sources->values()) {
|
||||
errors += value->init_dependencies(*g_sources);
|
||||
}
|
||||
BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting");
|
||||
UNUSED_VARS_NDEBUG(errors);
|
||||
|
||||
#if GPU_SHADER_PRINTF_ENABLE
|
||||
if (!g_formats->is_empty()) {
|
||||
/* Detect if there is any printf in node lib files.
|
||||
* See gpu_shader_dependency_force_gpu_print_injection(). */
|
||||
for (auto *value : g_sources->values()) {
|
||||
if (flag_is_set(value->builtins, shader::BuiltinBits::USE_PRINTF)) {
|
||||
if (value->filename.startswith("gpu_shader_material_")) {
|
||||
force_printf_injection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gpu_shader_dependency_exit()
|
||||
{
|
||||
for (auto *value : g_sources->values()) {
|
||||
delete value;
|
||||
}
|
||||
for (auto *value : g_functions->values()) {
|
||||
MEM_delete(value);
|
||||
}
|
||||
delete g_formats;
|
||||
delete g_sources;
|
||||
delete g_functions;
|
||||
g_formats = nullptr;
|
||||
g_sources = nullptr;
|
||||
g_functions = nullptr;
|
||||
}
|
||||
|
||||
GPUFunction *gpu_material_library_get_function(const char *name)
|
||||
{
|
||||
GPUFunction *function = g_functions->lookup_default(name, nullptr);
|
||||
BLI_assert_msg(function != nullptr, "Requested function not in the function library");
|
||||
return function;
|
||||
}
|
||||
|
||||
void gpu_material_library_use_function(Set<StringRefNull> &used_libraries, const char *name)
|
||||
{
|
||||
GPUFunction *function = g_functions->lookup_default(name, nullptr);
|
||||
BLI_assert_msg(function != nullptr, "Requested function not in the function library");
|
||||
GPUSource *source = reinterpret_cast<GPUSource *>(function->source);
|
||||
used_libraries.add(source->filename.c_str());
|
||||
}
|
||||
|
||||
namespace gpu::shader {
|
||||
|
||||
bool gpu_shader_dependency_force_gpu_print_injection()
|
||||
{
|
||||
/* WORKAROUND: We cannot know what shader will require printing if the printf is inside shader
|
||||
* node code. In this case, we just force injection inside all shaders. */
|
||||
return force_printf_injection;
|
||||
}
|
||||
|
||||
bool gpu_shader_dependency_has_printf()
|
||||
{
|
||||
return (g_formats != nullptr) && g_shader_use_printf;
|
||||
}
|
||||
|
||||
const PrintfFormat &gpu_shader_dependency_get_printf_format(uint32_t format_hash)
|
||||
{
|
||||
return g_formats->lookup(format_hash);
|
||||
}
|
||||
|
||||
BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull shader_source_name)
|
||||
{
|
||||
if (shader_source_name.is_empty()) {
|
||||
return shader::BuiltinBits::NONE;
|
||||
}
|
||||
if (g_sources->contains(shader_source_name) == false) {
|
||||
std::cerr << "Error: Could not find \"" << shader_source_name
|
||||
<< "\" in the list of registered source.\n";
|
||||
BLI_assert(0);
|
||||
return shader::BuiltinBits::NONE;
|
||||
}
|
||||
GPUSource *source = g_sources->lookup(shader_source_name);
|
||||
return source->builtins_get();
|
||||
}
|
||||
|
||||
Span<ShaderCreateInfo::SharedVariable> gpu_shader_dependency_get_shared_variables(
|
||||
const StringRefNull shader_source_name)
|
||||
{
|
||||
if (shader_source_name.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
if (g_sources->contains(shader_source_name) == false) {
|
||||
std::cerr << "Error: Could not find \"" << shader_source_name
|
||||
<< "\" in the list of registered source.\n";
|
||||
BLI_assert(0);
|
||||
return {};
|
||||
}
|
||||
GPUSource *source = g_sources->lookup(shader_source_name);
|
||||
return source->shared_variables;
|
||||
}
|
||||
|
||||
Vector<StringRefNull> gpu_shader_dependency_get_resolved_source(
|
||||
const StringRefNull shader_source_name,
|
||||
const shader::GeneratedSourceList &generated_sources,
|
||||
const StringRefNull shader_name)
|
||||
{
|
||||
Vector<StringRefNull> result;
|
||||
GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr);
|
||||
if (src == nullptr) {
|
||||
std::cerr << "Error source not found : " << shader_source_name << std::endl;
|
||||
}
|
||||
CLOG_TRACE(&LOG, "Resolved Source Tree (Mermaid flowchart) %s", shader_name.c_str());
|
||||
if (CLOG_CHECK(&LOG, CLG_LEVEL_TRACE)) {
|
||||
CLG_log_raw(LOG.type, "flowchart LR\n");
|
||||
}
|
||||
src->build(result, generated_sources, *g_sources);
|
||||
if (CLOG_CHECK(&LOG, CLG_LEVEL_TRACE)) {
|
||||
CLG_log_raw(LOG.type, "\n");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name)
|
||||
{
|
||||
GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr);
|
||||
if (src == nullptr) {
|
||||
std::cerr << "Error source not found : " << shader_source_name << std::endl;
|
||||
}
|
||||
return src->source;
|
||||
}
|
||||
|
||||
StringRefNull gpu_shader_dependency_get_filename_from_source_string(const StringRef source_string)
|
||||
{
|
||||
for (auto &source : g_sources->values()) {
|
||||
if (source->source == source_string) {
|
||||
return source->filename;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace gpu::shader
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Shader source dependency builder that make possible to support #include directive inside the
|
||||
* shader files.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void gpu_shader_dependency_init();
|
||||
|
||||
void gpu_shader_dependency_exit();
|
||||
|
||||
namespace gpu::shader {
|
||||
|
||||
BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull source_name);
|
||||
Span<ShaderCreateInfo::SharedVariable> gpu_shader_dependency_get_shared_variables(
|
||||
const StringRefNull shader_source_name);
|
||||
|
||||
/* Returns true is any shader code has a printf statement. */
|
||||
bool gpu_shader_dependency_has_printf();
|
||||
|
||||
bool gpu_shader_dependency_force_gpu_print_injection();
|
||||
|
||||
struct PrintfFormat {
|
||||
struct Block {
|
||||
enum ArgumentType {
|
||||
NONE = 0,
|
||||
STRING,
|
||||
UINT,
|
||||
INT,
|
||||
FLOAT,
|
||||
} type = NONE;
|
||||
std::string fmt;
|
||||
};
|
||||
|
||||
Vector<Block> format_blocks;
|
||||
std::string format_str;
|
||||
};
|
||||
|
||||
const PrintfFormat &gpu_shader_dependency_get_printf_format(uint32_t format_hash);
|
||||
|
||||
Vector<StringRefNull> gpu_shader_dependency_get_resolved_source(
|
||||
StringRefNull source_name,
|
||||
const GeneratedSourceList &generated_sources,
|
||||
StringRefNull shader_name = "");
|
||||
StringRefNull gpu_shader_dependency_get_source(StringRefNull source_name);
|
||||
|
||||
/**
|
||||
* \brief Find the name of the file from which the given string was generated.
|
||||
* \return filename or empty string.
|
||||
* \note source_string needs to be identical to the one given by gpu_shader_dependency_get_source()
|
||||
*/
|
||||
StringRefNull gpu_shader_dependency_get_filename_from_source_string(StringRef source_string);
|
||||
|
||||
} // namespace gpu::shader
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,9 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Intentionally empty for compiling shader builder.
|
||||
*/
|
||||
148
blender-5.2.0/source/blender/gpu/intern/gpu_shader_interface.cc
Normal file
148
blender-5.2.0/source/blender/gpu/intern/gpu_shader_interface.cc
Normal file
@@ -0,0 +1,148 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU shader interface (C --> GLSL)
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_interface.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* TODO(fclem): add unique ID for debugging. */
|
||||
ShaderInterface::ShaderInterface()
|
||||
{
|
||||
image_formats_.fill(TextureWriteFormat::Invalid);
|
||||
}
|
||||
|
||||
ShaderInterface::~ShaderInterface()
|
||||
{
|
||||
/* Free memory used by name_buffer. */
|
||||
MEM_SAFE_DELETE(name_buffer_);
|
||||
MEM_SAFE_DELETE(inputs_);
|
||||
}
|
||||
|
||||
static void sort_input_list(MutableSpan<ShaderInput> dst)
|
||||
{
|
||||
if (dst.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<ShaderInput> inputs_vec = Vector<ShaderInput>(dst.size());
|
||||
MutableSpan<ShaderInput> src = inputs_vec.as_mutable_span();
|
||||
src.copy_from(dst);
|
||||
|
||||
/* Simple sorting by going through the array and selecting the biggest element each time. */
|
||||
for (uint i = 0; i < dst.size(); i++) {
|
||||
ShaderInput *input_src = src.data();
|
||||
for (uint j = 1; j < src.size(); j++) {
|
||||
if (src[j].name_hash > input_src->name_hash) {
|
||||
input_src = &src[j];
|
||||
}
|
||||
}
|
||||
dst[i] = *input_src;
|
||||
input_src->name_hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderInterface::sort_inputs()
|
||||
{
|
||||
/* Sorts all inputs inside their respective array.
|
||||
* This is to allow fast hash collision detection.
|
||||
* See `ShaderInterface::input_lookup` for more details. */
|
||||
uint offset = 0;
|
||||
sort_input_list(MutableSpan<ShaderInput>(inputs_ + offset, attr_len_));
|
||||
offset += attr_len_;
|
||||
sort_input_list(MutableSpan<ShaderInput>(inputs_ + offset, ubo_len_));
|
||||
offset += ubo_len_;
|
||||
sort_input_list(MutableSpan<ShaderInput>(inputs_ + offset, uniform_len_));
|
||||
offset += uniform_len_;
|
||||
sort_input_list(MutableSpan<ShaderInput>(inputs_ + offset, ssbo_len_));
|
||||
offset += ssbo_len_;
|
||||
sort_input_list(MutableSpan<ShaderInput>(inputs_ + offset, constant_len_));
|
||||
offset += constant_len_;
|
||||
}
|
||||
|
||||
void ShaderInterface::set_image_formats_from_info(const shader::ShaderCreateInfo &info)
|
||||
{
|
||||
for (const shader::ShaderCreateInfo::Resource &res : info.pass_resources_) {
|
||||
if (res.bind_type == shader::ShaderCreateInfo::Resource::BindType::IMAGE) {
|
||||
image_formats_[res.slot] = TextureWriteFormat(res.image.format);
|
||||
}
|
||||
}
|
||||
for (const shader::ShaderCreateInfo::Resource &res : info.batch_resources_) {
|
||||
if (res.bind_type == shader::ShaderCreateInfo::Resource::BindType::IMAGE) {
|
||||
image_formats_[res.slot] = TextureWriteFormat(res.image.format);
|
||||
}
|
||||
}
|
||||
for (const shader::ShaderCreateInfo::Resource &res : info.geometry_resources_) {
|
||||
if (res.bind_type == shader::ShaderCreateInfo::Resource::BindType::IMAGE) {
|
||||
image_formats_[res.slot] = TextureWriteFormat(res.image.format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderInterface::debug_print() const
|
||||
{
|
||||
Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_);
|
||||
Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_);
|
||||
Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_);
|
||||
Span<ShaderInput> ssbos = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_ + uniform_len_,
|
||||
ssbo_len_);
|
||||
char *name_buf = name_buffer_;
|
||||
const char format[] = " | %.8x : %4d : %s\n";
|
||||
|
||||
if (attrs.size() > 0) {
|
||||
printf("\n Attributes :\n");
|
||||
}
|
||||
for (const ShaderInput &attr : attrs) {
|
||||
printf(format, attr.name_hash, attr.location, name_buf + attr.name_offset);
|
||||
}
|
||||
|
||||
if (uniforms.size() > 0) {
|
||||
printf("\n Uniforms :\n");
|
||||
}
|
||||
for (const ShaderInput &uni : uniforms) {
|
||||
/* Bypass samplers. */
|
||||
if (uni.binding == -1) {
|
||||
printf(format, uni.name_hash, uni.location, name_buf + uni.name_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (ubos.size() > 0) {
|
||||
printf("\n Uniform Buffer Objects :\n");
|
||||
}
|
||||
for (const ShaderInput &ubo : ubos) {
|
||||
printf(format, ubo.name_hash, ubo.binding, name_buf + ubo.name_offset);
|
||||
}
|
||||
|
||||
if (enabled_tex_mask_ > 0) {
|
||||
printf("\n Samplers :\n");
|
||||
}
|
||||
for (const ShaderInput &samp : uniforms) {
|
||||
/* Bypass uniforms. */
|
||||
if (samp.binding != -1) {
|
||||
printf(format, samp.name_hash, samp.binding, name_buf + samp.name_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (ssbos.size() > 0) {
|
||||
printf("\n Shader Storage Objects :\n");
|
||||
}
|
||||
for (const ShaderInput &ssbo : ssbos) {
|
||||
printf(format, ssbo.name_hash, ssbo.binding, name_buf + ssbo.name_offset);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
360
blender-5.2.0/source/blender/gpu/intern/gpu_shader_interface.hh
Normal file
360
blender-5.2.0/source/blender/gpu/intern/gpu_shader_interface.hh
Normal file
@@ -0,0 +1,360 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU shader interface (C --> GLSL)
|
||||
*
|
||||
* Structure detailing needed vertex inputs and resources for a specific shader.
|
||||
* A shader interface can be shared between two similar shaders.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring> /* required for STREQ later on. */
|
||||
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "GPU_format.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_vertex_format.hh" /* GPU_VERT_ATTR_MAX_LEN */
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
struct ShaderInput {
|
||||
uint32_t name_offset;
|
||||
uint32_t name_hash;
|
||||
/**
|
||||
* Location is openGl legacy and its legacy usages should be phased out in Blender 3.7.
|
||||
*
|
||||
* Vulkan backend use location to encode the descriptor set binding. This binding is different
|
||||
* than the binding stored in the binding attribute. In Vulkan the binding inside a descriptor
|
||||
* set must be unique. In future the location will also be used to select the right descriptor
|
||||
* set.
|
||||
*/
|
||||
int32_t location;
|
||||
/** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attributes. */
|
||||
int32_t binding;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of Shader interface.
|
||||
* Base class which is then specialized for each implementation (GL, VK, ...).
|
||||
*/
|
||||
class ShaderInterface {
|
||||
friend shader::ShaderCreateInfo;
|
||||
/* TODO(fclem): should be protected. */
|
||||
public:
|
||||
/** Flat array. In this order: Attributes, Ubos, Uniforms, SSBOs, Constants. */
|
||||
ShaderInput *inputs_ = nullptr;
|
||||
/** Buffer containing all inputs names separated by '\0'. */
|
||||
char *name_buffer_ = nullptr;
|
||||
/** Input counts inside input array. */
|
||||
uint attr_len_ = 0;
|
||||
uint ubo_len_ = 0;
|
||||
uint uniform_len_ = 0;
|
||||
uint ssbo_len_ = 0;
|
||||
uint constant_len_ = 0;
|
||||
/** Enabled bind-points that needs to be fed with data. */
|
||||
uint16_t enabled_attr_mask_ = 0;
|
||||
uint16_t enabled_ubo_mask_ = 0;
|
||||
uint8_t enabled_ima_mask_ = 0;
|
||||
uint64_t enabled_tex_mask_ = 0;
|
||||
uint16_t enabled_ssbo_mask_ = 0;
|
||||
/* Bitmask to apply to enabled_ssbo_mask_ to get attributes that are sourced from SSBOs. */
|
||||
uint16_t ssbo_attr_mask_ = 0;
|
||||
/** Location of builtin uniforms. Fast access, no lookup needed. */
|
||||
int32_t builtins_[GPU_NUM_UNIFORMS];
|
||||
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
|
||||
|
||||
/**
|
||||
* Currently only used for `GPU_shader_get_attribute_info`.
|
||||
* This utility is useful for automatic creation of `GPUVertFormat` in Python.
|
||||
* Use `ShaderInput::location` to identify the `Type`.
|
||||
*/
|
||||
uint8_t attr_types_[GPU_VERT_ATTR_MAX_LEN];
|
||||
|
||||
/* Formats of all image units. */
|
||||
std::array<TextureWriteFormat, GPU_MAX_IMAGE> image_formats_;
|
||||
|
||||
ShaderInterface();
|
||||
virtual ~ShaderInterface();
|
||||
|
||||
void debug_print() const;
|
||||
|
||||
const ShaderInput *attr_get(const StringRefNull name) const
|
||||
{
|
||||
return input_lookup(inputs_, attr_len_, name);
|
||||
}
|
||||
const ShaderInput *attr_get(const int binding) const
|
||||
{
|
||||
return input_lookup(inputs_, attr_len_, binding);
|
||||
}
|
||||
|
||||
const ShaderInput *ubo_get(const StringRefNull name) const
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_, ubo_len_, name);
|
||||
}
|
||||
const ShaderInput *ubo_get(const int binding) const
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_, ubo_len_, binding);
|
||||
}
|
||||
|
||||
const ShaderInput *uniform_get(const StringRefNull name) const
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, name);
|
||||
}
|
||||
|
||||
const ShaderInput *texture_get(const int binding) const
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, binding);
|
||||
}
|
||||
|
||||
const ShaderInput *ssbo_get(const StringRefNull name) const
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name);
|
||||
}
|
||||
const ShaderInput *ssbo_get(const int binding) const
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, binding);
|
||||
}
|
||||
|
||||
const ShaderInput *constant_get(const StringRefNull name) const
|
||||
{
|
||||
return input_lookup(
|
||||
inputs_ + attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_, constant_len_, name);
|
||||
}
|
||||
|
||||
const char *input_name_get(const ShaderInput *input) const
|
||||
{
|
||||
return name_buffer_ + input->name_offset;
|
||||
}
|
||||
|
||||
/* Returns uniform location. */
|
||||
int32_t uniform_builtin(const GPUUniformBuiltin builtin) const
|
||||
{
|
||||
BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS);
|
||||
return builtins_[builtin];
|
||||
}
|
||||
|
||||
/* Returns binding position. */
|
||||
int32_t ubo_builtin(const GPUUniformBlockBuiltin builtin) const
|
||||
{
|
||||
BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS);
|
||||
return builtin_blocks_[builtin];
|
||||
}
|
||||
|
||||
inline uint valid_bindings_get(const ShaderInput *const inputs, const uint inputs_len) const;
|
||||
|
||||
bool attr_len_get() const
|
||||
{
|
||||
return attr_len_;
|
||||
}
|
||||
|
||||
bool ubo_len_get() const
|
||||
{
|
||||
return ubo_len_;
|
||||
}
|
||||
|
||||
bool uniform_len_get() const
|
||||
{
|
||||
return uniform_len_;
|
||||
}
|
||||
|
||||
bool ssbo_len_get() const
|
||||
{
|
||||
return ssbo_len_;
|
||||
}
|
||||
|
||||
bool constant_len_get() const
|
||||
{
|
||||
return constant_len_;
|
||||
}
|
||||
|
||||
protected:
|
||||
static inline const char *builtin_uniform_name(GPUUniformBuiltin u);
|
||||
static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u);
|
||||
|
||||
inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const;
|
||||
inline void copy_input_name(ShaderInput *input,
|
||||
const StringRefNull &name,
|
||||
char *name_buffer,
|
||||
uint32_t &name_buffer_offset) const;
|
||||
|
||||
/**
|
||||
* Finalize interface construction by sorting the #ShaderInputs for faster lookups.
|
||||
*/
|
||||
void sort_inputs();
|
||||
|
||||
void set_image_formats_from_info(const shader::ShaderCreateInfo &info);
|
||||
|
||||
private:
|
||||
inline const ShaderInput *input_lookup(const ShaderInput *const inputs,
|
||||
uint inputs_len,
|
||||
StringRefNull name) const;
|
||||
|
||||
inline const ShaderInput *input_lookup(const ShaderInput *const inputs,
|
||||
uint inputs_len,
|
||||
int binding) const;
|
||||
};
|
||||
|
||||
inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u)
|
||||
{
|
||||
switch (u) {
|
||||
case GPU_UNIFORM_MODEL:
|
||||
return "ModelMatrix";
|
||||
case GPU_UNIFORM_VIEW:
|
||||
return "ViewMatrix";
|
||||
case GPU_UNIFORM_MODELVIEW:
|
||||
return "ModelViewMatrix";
|
||||
case GPU_UNIFORM_PROJECTION:
|
||||
return "ProjectionMatrix";
|
||||
case GPU_UNIFORM_VIEWPROJECTION:
|
||||
return "ViewProjectionMatrix";
|
||||
case GPU_UNIFORM_MVP:
|
||||
return "ModelViewProjectionMatrix";
|
||||
|
||||
case GPU_UNIFORM_MODEL_INV:
|
||||
return "ModelMatrixInverse";
|
||||
case GPU_UNIFORM_VIEW_INV:
|
||||
return "ViewMatrixInverse";
|
||||
case GPU_UNIFORM_MODELVIEW_INV:
|
||||
return "ModelViewMatrixInverse";
|
||||
case GPU_UNIFORM_PROJECTION_INV:
|
||||
return "ProjectionMatrixInverse";
|
||||
case GPU_UNIFORM_VIEWPROJECTION_INV:
|
||||
return "ViewProjectionMatrixInverse";
|
||||
|
||||
case GPU_UNIFORM_NORMAL:
|
||||
return "NormalMatrix";
|
||||
case GPU_UNIFORM_CLIPPLANES:
|
||||
return "WorldClipPlanes";
|
||||
|
||||
case GPU_UNIFORM_COLOR:
|
||||
return "color";
|
||||
case GPU_UNIFORM_BASE_INSTANCE:
|
||||
return "gpu_BaseInstance";
|
||||
case GPU_UNIFORM_SRGB_TRANSFORM:
|
||||
return "srgbTarget";
|
||||
case GPU_UNIFORM_SCENE_LINEAR_XFORM:
|
||||
return "gpu_scene_linear_to_rec709";
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBuiltin u)
|
||||
{
|
||||
switch (u) {
|
||||
case GPU_UNIFORM_BLOCK_VIEW:
|
||||
return "viewBlock";
|
||||
case GPU_UNIFORM_BLOCK_MODEL:
|
||||
return "modelBlock";
|
||||
case GPU_UNIFORM_BLOCK_INFO:
|
||||
return "infoBlock";
|
||||
|
||||
case GPU_UNIFORM_BLOCK_DRW_VIEW:
|
||||
return "drw_view_";
|
||||
case GPU_UNIFORM_BLOCK_DRW_MODEL:
|
||||
return "drw_matrices";
|
||||
case GPU_UNIFORM_BLOCK_DRW_INFOS:
|
||||
return "drw_infos";
|
||||
case GPU_UNIFORM_BLOCK_DRW_CLIPPING:
|
||||
return "drw_clipping_";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns string length including '\0' terminator. */
|
||||
inline uint32_t ShaderInterface::set_input_name(ShaderInput *input,
|
||||
char *name,
|
||||
uint32_t name_len) const
|
||||
{
|
||||
/* remove "[0]" from array name */
|
||||
if (name[name_len - 1] == ']') {
|
||||
for (; name_len > 1; name_len--) {
|
||||
if (name[name_len] == '[') {
|
||||
name[name_len] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input->name_offset = uint32_t(name - name_buffer_);
|
||||
input->name_hash = BLI_hash_string(name);
|
||||
return name_len + 1; /* include NULL terminator */
|
||||
}
|
||||
|
||||
inline void ShaderInterface::copy_input_name(ShaderInput *input,
|
||||
const StringRefNull &name,
|
||||
char *name_buffer,
|
||||
uint32_t &name_buffer_offset) const
|
||||
{
|
||||
uint32_t name_len = name.size();
|
||||
/* Copy include NULL terminator. */
|
||||
memcpy(name_buffer + name_buffer_offset, name.c_str(), name_len + 1);
|
||||
name_buffer_offset += set_input_name(input, name_buffer + name_buffer_offset, name_len);
|
||||
}
|
||||
|
||||
inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs,
|
||||
const uint inputs_len,
|
||||
const StringRefNull name) const
|
||||
{
|
||||
const uint name_hash = BLI_hash_string(name.c_str());
|
||||
/* Simple linear search for now. */
|
||||
for (int i = inputs_len - 1; i >= 0; i--) {
|
||||
if (inputs[i].name_hash == name_hash) {
|
||||
if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) {
|
||||
/* Hash collision resolve. */
|
||||
for (; i >= 0 && inputs[i].name_hash == name_hash; i--) {
|
||||
if (name == (name_buffer_ + inputs[i].name_offset)) {
|
||||
return inputs + i; /* not found */
|
||||
}
|
||||
}
|
||||
return nullptr; /* not found */
|
||||
}
|
||||
|
||||
/* This is a bit dangerous since we could have a hash collision.
|
||||
* where the asked uniform that does not exist has the same hash
|
||||
* as a real uniform. */
|
||||
BLI_assert(name == (name_buffer_ + inputs[i].name_offset));
|
||||
return inputs + i;
|
||||
}
|
||||
}
|
||||
return nullptr; /* not found */
|
||||
}
|
||||
|
||||
inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs,
|
||||
const uint inputs_len,
|
||||
const int binding) const
|
||||
{
|
||||
/* Simple linear search for now. */
|
||||
for (int i = inputs_len - 1; i >= 0; i--) {
|
||||
if (inputs[i].binding == binding) {
|
||||
return inputs + i;
|
||||
}
|
||||
}
|
||||
return nullptr; /* not found */
|
||||
}
|
||||
|
||||
inline uint ShaderInterface::valid_bindings_get(const ShaderInput *const inputs,
|
||||
const uint inputs_len) const
|
||||
{
|
||||
/* Simple linear search for now. */
|
||||
int valid_bindings = 0;
|
||||
for (int i = inputs_len - 1; i >= 0; i--) {
|
||||
if (inputs[i].binding > -1) {
|
||||
valid_bindings++;
|
||||
}
|
||||
}
|
||||
return valid_bindings;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
466
blender-5.2.0/source/blender/gpu/intern/gpu_shader_log.cc
Normal file
466
blender-5.2.0/source/blender/gpu/intern/gpu_shader_log.cc
Normal file
@@ -0,0 +1,466 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_storage_buffer.hh"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_shader_dependency_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.shader"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug functions
|
||||
* \{ */
|
||||
|
||||
/* Number of lines before and after the error line to print for compilation errors. */
|
||||
#define DEBUG_CONTEXT_LINES 0
|
||||
/**
|
||||
* Print dependencies sources list before the shader report.
|
||||
* Useful to debug include order or missing dependencies.
|
||||
*/
|
||||
#define DEBUG_DEPENDENCIES 0
|
||||
|
||||
void Shader::print_log(Span<StringRefNull> sources,
|
||||
const char *log,
|
||||
const char *stage,
|
||||
const bool error,
|
||||
GPULogParser *parser)
|
||||
{
|
||||
const char line_prefix[] = " | ";
|
||||
char err_col[] = "\033[31;1m";
|
||||
char warn_col[] = "\033[33;1m";
|
||||
char info_col[] = "\033[0;2m";
|
||||
char reset_col[] = "\033[0;0m";
|
||||
std::string sources_combined = fmt::to_string(fmt::join(sources, ""));
|
||||
DynStr *dynstr = BLI_dynstr_new();
|
||||
|
||||
if (!CLG_color_support_get(&LOG)) {
|
||||
err_col[0] = warn_col[0] = info_col[0] = reset_col[0] = '\0';
|
||||
}
|
||||
|
||||
BLI_dynstr_appendf(dynstr, "\n");
|
||||
|
||||
#if DEBUG_DEPENDENCIES
|
||||
BLI_dynstr_appendf(
|
||||
dynstr, "%s%sIncluded files (in order):%s\n", info_col, line_prefix, reset_col);
|
||||
#endif
|
||||
|
||||
Vector<int64_t> sources_end_line;
|
||||
for (StringRef src : sources) {
|
||||
int64_t cursor = 0, line_count = 0;
|
||||
while ((cursor = src.find('\n', cursor) + 1)) {
|
||||
line_count++;
|
||||
}
|
||||
if (sources_end_line.is_empty() == false) {
|
||||
line_count += sources_end_line.last();
|
||||
}
|
||||
sources_end_line.append(line_count);
|
||||
#if DEBUG_DEPENDENCIES
|
||||
StringRefNull filename = shader::gpu_shader_dependency_get_filename_from_source_string(src);
|
||||
if (!filename.is_empty()) {
|
||||
BLI_dynstr_appendf(
|
||||
dynstr, "%s%s %s%s\n", info_col, line_prefix, filename.c_str(), reset_col);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (sources_end_line.is_empty()) {
|
||||
sources_end_line.append(0);
|
||||
}
|
||||
|
||||
const char *log_line = log, *line_end;
|
||||
|
||||
LogCursor previous_location;
|
||||
|
||||
bool found_line_id = false;
|
||||
while ((line_end = strchr(log_line, '\n'))) {
|
||||
/* Skip empty lines. */
|
||||
if (line_end == log_line) {
|
||||
log_line++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Silence not useful lines. */
|
||||
StringRef logref = StringRefNull(log_line).substr(0, size_t(line_end) - size_t(log_line));
|
||||
if (logref.endswith(" shader failed to compile with the following errors:") ||
|
||||
logref.endswith(" No code generated"))
|
||||
{
|
||||
log_line += size_t(line_end) - size_t(log_line);
|
||||
continue;
|
||||
}
|
||||
|
||||
GPULogItem log_item;
|
||||
log_line = parser->parse_line(sources_combined.c_str(), log_line, log_item);
|
||||
|
||||
/* Empty line, skip. */
|
||||
if ((log_item.cursor.row == -1) && ELEM(log_line[0], '\n', '\0')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Sanitize output. Really bad values can happen when the error line is buggy. */
|
||||
if (log_item.cursor.source >= sources.size()) {
|
||||
log_item.cursor.source = -1;
|
||||
}
|
||||
if (log_item.cursor.row >= sources_end_line.last()) {
|
||||
log_item.cursor.source = -1;
|
||||
log_item.cursor.row = -1;
|
||||
}
|
||||
|
||||
if (log_item.cursor.row == -1) {
|
||||
found_line_id = false;
|
||||
}
|
||||
|
||||
const char *src_line = sources_combined.c_str();
|
||||
|
||||
std::string log_line_str(log_line, (line_end + 1) - log_line);
|
||||
if (log_line_str.ends_with(" used uninitialized\n")) {
|
||||
/* Mesa GLSL compiler warns about uninitialized variables are mostly false positive.
|
||||
* Avoid noise. */
|
||||
log_line = line_end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Separate from previous block. */
|
||||
if (previous_location.source != log_item.cursor.source ||
|
||||
previous_location.row != log_item.cursor.row)
|
||||
{
|
||||
BLI_dynstr_appendf(dynstr, "%s%s%s\n", info_col, line_prefix, reset_col);
|
||||
}
|
||||
else if (log_item.cursor.column != previous_location.column) {
|
||||
BLI_dynstr_appendf(dynstr, "%s\n", line_prefix);
|
||||
}
|
||||
/* Print line from the source file that is producing the error. */
|
||||
if ((log_item.cursor.row != -1) && (log_item.cursor.row != previous_location.row ||
|
||||
log_item.cursor.column != previous_location.column))
|
||||
{
|
||||
const char *src_line_end;
|
||||
found_line_id = false;
|
||||
/* Lines are 1 based. */
|
||||
int src_line_index = 1;
|
||||
while ((src_line_end = strchr(src_line, '\n'))) {
|
||||
if (src_line_index >= log_item.cursor.row) {
|
||||
found_line_id = true;
|
||||
break;
|
||||
}
|
||||
if (src_line_index >= log_item.cursor.row - DEBUG_CONTEXT_LINES) {
|
||||
BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
|
||||
BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
|
||||
}
|
||||
/* Continue to next line. */
|
||||
src_line = src_line_end + 1;
|
||||
src_line_index++;
|
||||
}
|
||||
/* Print error source. */
|
||||
if (found_line_id) {
|
||||
if (log_item.cursor.row != previous_location.row) {
|
||||
BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
|
||||
}
|
||||
else {
|
||||
BLI_dynstr_appendf(dynstr, line_prefix);
|
||||
}
|
||||
BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
|
||||
/* Print char offset. */
|
||||
BLI_dynstr_appendf(dynstr, line_prefix);
|
||||
if (log_item.cursor.column != -1) {
|
||||
for (int i = 0; i < log_item.cursor.column; i++) {
|
||||
BLI_dynstr_appendf(dynstr, " ");
|
||||
}
|
||||
BLI_dynstr_appendf(dynstr, "^");
|
||||
}
|
||||
BLI_dynstr_appendf(dynstr, "\n");
|
||||
|
||||
/* Skip the error line. */
|
||||
src_line = src_line_end + 1;
|
||||
src_line_index++;
|
||||
while ((src_line_end = strchr(src_line, '\n'))) {
|
||||
if (src_line_index > log_item.cursor.row + DEBUG_CONTEXT_LINES) {
|
||||
break;
|
||||
}
|
||||
BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index);
|
||||
BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line);
|
||||
/* Continue to next line. */
|
||||
src_line = src_line_end + 1;
|
||||
src_line_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
BLI_dynstr_appendf(dynstr, line_prefix);
|
||||
|
||||
/* Search the correct source index. */
|
||||
int row_in_file = log_item.cursor.row;
|
||||
int source_index = log_item.cursor.source;
|
||||
if (source_index <= 0) {
|
||||
for (auto i : sources_end_line.index_range()) {
|
||||
if (log_item.cursor.row <= sources_end_line[i]) {
|
||||
source_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (source_index > 0) {
|
||||
row_in_file -= sources_end_line[source_index - 1];
|
||||
}
|
||||
/* Print the filename the error line is coming from. */
|
||||
if (!log_item.cursor.file_name_and_error_line.empty()) {
|
||||
char name_buf[256];
|
||||
std::string string = log_item.cursor.file_name_and_error_line.substr(0,
|
||||
sizeof(name_buf) - 1);
|
||||
StringRefNull(string).copy_utf8_truncated(name_buf);
|
||||
BLI_dynstr_appendf(dynstr, "%s%s: %s", info_col, name_buf, reset_col);
|
||||
}
|
||||
else if (source_index > 0) {
|
||||
StringRefNull filename = shader::gpu_shader_dependency_get_filename_from_source_string(
|
||||
sources[source_index]);
|
||||
if (!filename.is_empty()) {
|
||||
BLI_dynstr_appendf(dynstr,
|
||||
"%s%s:%d:%d: %s",
|
||||
info_col,
|
||||
filename.c_str(),
|
||||
row_in_file,
|
||||
log_item.cursor.column + 1,
|
||||
reset_col);
|
||||
}
|
||||
}
|
||||
|
||||
if (log_item.severity == Severity::Error) {
|
||||
BLI_dynstr_appendf(dynstr, "%s%s%s: ", err_col, "Error", info_col);
|
||||
}
|
||||
else if (log_item.severity == Severity::Warning) {
|
||||
BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Warning", info_col);
|
||||
}
|
||||
else if (log_item.severity == Severity::Note) {
|
||||
BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Note", info_col);
|
||||
}
|
||||
/* Print the error itself. */
|
||||
BLI_dynstr_append(dynstr, info_col);
|
||||
BLI_dynstr_nappend(dynstr, log_line, (line_end + 1) - log_line);
|
||||
BLI_dynstr_append(dynstr, reset_col);
|
||||
/* Continue to next line. */
|
||||
log_line = line_end + 1;
|
||||
previous_location = log_item.cursor;
|
||||
}
|
||||
|
||||
CLG_Level level = error ? CLG_LEVEL_ERROR : CLG_LEVEL_WARN;
|
||||
|
||||
if (CLOG_CHECK(&LOG, level)) {
|
||||
if (DEBUG_LOG_SHADER_SRC_ON_ERROR && error) {
|
||||
CLG_log_raw(LOG.type, sources_combined.c_str());
|
||||
}
|
||||
const char *_str = BLI_dynstr_get_cstring(dynstr);
|
||||
CLOG_AT_LEVEL(&LOG, level, "%s %s: %s", this->name, stage, _str);
|
||||
MEM_delete(_str);
|
||||
}
|
||||
|
||||
BLI_dynstr_free(dynstr);
|
||||
}
|
||||
|
||||
const char *GPULogParser::skip_severity(const char *log_line,
|
||||
GPULogItem &log_item,
|
||||
const char *error_msg,
|
||||
const char *warning_msg,
|
||||
const char *note_msg) const
|
||||
{
|
||||
if (STREQLEN(log_line, error_msg, strlen(error_msg))) {
|
||||
log_line += strlen(error_msg);
|
||||
log_item.severity = Severity::Error;
|
||||
}
|
||||
else if (STREQLEN(log_line, warning_msg, strlen(warning_msg))) {
|
||||
log_line += strlen(warning_msg);
|
||||
log_item.severity = Severity::Warning;
|
||||
}
|
||||
else if (STREQLEN(log_line, note_msg, strlen(note_msg))) {
|
||||
log_line += strlen(note_msg);
|
||||
log_item.severity = Severity::Note;
|
||||
}
|
||||
return log_line;
|
||||
}
|
||||
|
||||
const char *GPULogParser::skip_separators(const char *log_line, const StringRef separators) const
|
||||
{
|
||||
while (at_any(log_line, separators)) {
|
||||
log_line++;
|
||||
}
|
||||
return log_line;
|
||||
}
|
||||
|
||||
const char *GPULogParser::skip_until(const char *log_line, char stop_char) const
|
||||
{
|
||||
const char *cursor = log_line;
|
||||
while (!ELEM(cursor[0], '\n', '\0')) {
|
||||
if (cursor[0] == stop_char) {
|
||||
return cursor;
|
||||
}
|
||||
cursor++;
|
||||
}
|
||||
return log_line;
|
||||
}
|
||||
|
||||
bool GPULogParser::at_number(const char *log_line) const
|
||||
{
|
||||
return log_line[0] >= '0' && log_line[0] <= '9';
|
||||
}
|
||||
|
||||
bool GPULogParser::at_any(const char *log_line, const StringRef chars) const
|
||||
{
|
||||
return chars.find(log_line[0]) != StringRef::not_found;
|
||||
}
|
||||
|
||||
int GPULogParser::parse_number(const char *log_line, const char **r_new_position) const
|
||||
{
|
||||
return int(strtol(log_line, const_cast<char **>(r_new_position), 10));
|
||||
}
|
||||
|
||||
size_t GPULogParser::line_start_get(StringRefNull source_combined, size_t target_line)
|
||||
{
|
||||
size_t cursor = 0;
|
||||
size_t current_line = 1;
|
||||
for (char c : source_combined) {
|
||||
if (current_line >= target_line) {
|
||||
return cursor + 1;
|
||||
}
|
||||
if (c == '\n') {
|
||||
current_line++;
|
||||
}
|
||||
cursor++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
StringRef GPULogParser::filename_get(StringRefNull source_combined, size_t pos)
|
||||
{
|
||||
StringRef sub_str = source_combined.substr(0, pos);
|
||||
StringRefNull directive = "#line 1 \"";
|
||||
size_t nearest_line_directive = sub_str.rfind(directive);
|
||||
if (nearest_line_directive != std::string::npos) {
|
||||
size_t start_of_file_name = nearest_line_directive + directive.size();
|
||||
size_t end_of_file_name = sub_str.find('\"', start_of_file_name);
|
||||
if (end_of_file_name != std::string::npos) {
|
||||
return sub_str.substr(start_of_file_name, end_of_file_name - start_of_file_name);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/* Original source file line. Found by looking up #line directives. */
|
||||
size_t GPULogParser::source_line_get(StringRefNull source_combined, size_t pos)
|
||||
{
|
||||
StringRef sub_str = source_combined.substr(0, pos);
|
||||
StringRefNull directive = "#line ";
|
||||
size_t nearest_line_directive = sub_str.rfind(directive);
|
||||
size_t line_count = 1;
|
||||
if (nearest_line_directive != std::string::npos) {
|
||||
sub_str = sub_str.substr(nearest_line_directive + directive.size());
|
||||
line_count = std::stoll(sub_str) - 1;
|
||||
}
|
||||
return line_count + std::count(sub_str.begin(), sub_str.end(), '\n');
|
||||
}
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shader Debug Printf
|
||||
* \{ */
|
||||
|
||||
void printf_begin(Context *ctx)
|
||||
{
|
||||
#if GPU_SHADER_PRINTF_ENABLE == 0
|
||||
return;
|
||||
#endif
|
||||
if (ctx == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!shader::gpu_shader_dependency_has_printf()) {
|
||||
return;
|
||||
}
|
||||
StorageBuf *printf_buf = GPU_storagebuf_create(GPU_SHADER_PRINTF_MAX_CAPACITY *
|
||||
sizeof(uint32_t));
|
||||
GPU_storagebuf_clear_to_zero(printf_buf);
|
||||
ctx->printf_buf.append(printf_buf);
|
||||
}
|
||||
|
||||
void printf_end(Context *ctx)
|
||||
{
|
||||
#if GPU_SHADER_PRINTF_ENABLE == 0
|
||||
return;
|
||||
#endif
|
||||
if (ctx == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (ctx->printf_buf.is_empty()) {
|
||||
return;
|
||||
}
|
||||
StorageBuf *printf_buf = ctx->printf_buf.pop_last();
|
||||
|
||||
Vector<uint32_t> data(GPU_SHADER_PRINTF_MAX_CAPACITY);
|
||||
GPU_storagebuf_read(printf_buf, data.data());
|
||||
GPU_storagebuf_free(printf_buf);
|
||||
|
||||
uint32_t data_len = data[0];
|
||||
if (data_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* data[0] contains the length of the data. */
|
||||
int cursor = 1;
|
||||
while (cursor < data_len + 1) {
|
||||
uint32_t format_hash = data[cursor++];
|
||||
|
||||
const shader::PrintfFormat &format = shader::gpu_shader_dependency_get_printf_format(
|
||||
format_hash);
|
||||
|
||||
if (cursor + format.format_blocks.size() >= GPU_SHADER_PRINTF_MAX_CAPACITY) {
|
||||
printf("Printf buffer overflow.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
for (const shader::PrintfFormat::Block &block : format.format_blocks) {
|
||||
switch (block.type) {
|
||||
case shader::PrintfFormat::Block::NONE:
|
||||
printf("%s", block.fmt.c_str());
|
||||
break;
|
||||
case shader::PrintfFormat::Block::STRING: {
|
||||
/* This is actually just the string that is referenced, not a format. */
|
||||
const shader::PrintfFormat &str = shader::gpu_shader_dependency_get_printf_format(
|
||||
*reinterpret_cast<uint32_t *>(&data[cursor++]));
|
||||
printf(block.fmt.c_str(), str.format_str.c_str());
|
||||
break;
|
||||
}
|
||||
case shader::PrintfFormat::Block::UINT:
|
||||
printf(block.fmt.c_str(), *reinterpret_cast<uint32_t *>(&data[cursor++]));
|
||||
break;
|
||||
case shader::PrintfFormat::Block::INT:
|
||||
printf(block.fmt.c_str(), *reinterpret_cast<int32_t *>(&data[cursor++]));
|
||||
break;
|
||||
case shader::PrintfFormat::Block::FLOAT:
|
||||
printf(block.fmt.c_str(), *reinterpret_cast<float *>(&data[cursor++]));
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Shader source dependency builder that make possible to support #include directive inside the
|
||||
* shader files.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "shader_tool/metadata.hh"
|
||||
|
||||
namespace blender::gpu::shader {
|
||||
|
||||
BuiltinBits convert_builtin_bit(metadata::Builtin builtin);
|
||||
|
||||
} // namespace blender::gpu::shader
|
||||
1671
blender-5.2.0/source/blender/gpu/intern/gpu_shader_preprocessor.cc
Normal file
1671
blender-5.2.0/source/blender/gpu/intern/gpu_shader_preprocessor.cc
Normal file
File diff suppressed because it is too large
Load Diff
297
blender-5.2.0/source/blender/gpu/intern/gpu_shader_private.hh
Normal file
297
blender-5.2.0/source/blender/gpu/intern/gpu_shader_private.hh
Normal file
@@ -0,0 +1,297 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_worker.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_interface.hh"
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class GPULogParser;
|
||||
class Context;
|
||||
|
||||
/* Set to 1 to log the full source of shaders that fail to compile. */
|
||||
#define DEBUG_LOG_SHADER_SRC_ON_ERROR 0
|
||||
|
||||
/**
|
||||
* Compilation is done on a list of GLSL sources. This list contains placeholders that should be
|
||||
* provided by the backend shader. These defines contains the locations where the backend can patch
|
||||
* the sources.
|
||||
*/
|
||||
#define SOURCES_INDEX_VERSION 0
|
||||
#define SOURCES_INDEX_SPECIALIZATION_CONSTANTS 1
|
||||
|
||||
struct PatchedShaderCreateInfo {
|
||||
shader::ShaderCreateInfo info;
|
||||
shader::ShaderCreateInfoStringCache names;
|
||||
|
||||
PatchedShaderCreateInfo(const shader::ShaderCreateInfo &info_) : info(info_) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of shader compilation and uniforms handling.
|
||||
* Base class which is then specialized for each implementation (GL, VK, ...).
|
||||
*/
|
||||
class Shader {
|
||||
public:
|
||||
/** Uniform & attribute locations for shader. */
|
||||
ShaderInterface *interface = nullptr;
|
||||
/** Bit-set indicating the frame-buffer color attachments that this shader writes to. */
|
||||
uint16_t fragment_output_bits = 0;
|
||||
|
||||
/* Default specialization constants state as defined inside ShaderCreateInfo.
|
||||
* Should be considered as const after init(). */
|
||||
std::unique_ptr<const shader::SpecializationConstants> constants;
|
||||
|
||||
/* WORKAROUND: True if this shader is a polyline shader and needs an appropriate setup to render.
|
||||
* Eventually, in the future, we should modify the user code instead of relying on such hacks. */
|
||||
bool is_polyline = false;
|
||||
/* WORKAROUND: Skip preprocessor for shader that we can't ensure the content.
|
||||
* This avoid crashes until we make the preprocessor robust to errors.
|
||||
* Also the preprocessor doesn't support forward declaration at the moment and needs manual
|
||||
* tweaking. */
|
||||
bool skip_preprocessor = false;
|
||||
|
||||
protected:
|
||||
/** For debugging purpose. */
|
||||
char name[64];
|
||||
|
||||
/* Parent shader can be used for shaders which are derived from the same source material.
|
||||
* The child shader can pull information from its parent to prepare additional resources
|
||||
* such as PSOs upfront. This enables asynchronous PSO compilation which mitigates stuttering
|
||||
* when updating new materials. */
|
||||
Shader *parent_shader_ = nullptr;
|
||||
|
||||
/* In some situation, a backend might want to transform the create infos before it is being
|
||||
* parsed. */
|
||||
std::unique_ptr<PatchedShaderCreateInfo> patched_info_;
|
||||
|
||||
public:
|
||||
Shader(const char *name);
|
||||
virtual ~Shader();
|
||||
|
||||
virtual void init(const shader::ShaderCreateInfo &info, bool is_codegen_only) = 0;
|
||||
|
||||
/* Patch create infos for any additional resources that could be needed. */
|
||||
virtual const shader::ShaderCreateInfo &patch_create_info(
|
||||
const shader::ShaderCreateInfo &original_info) = 0;
|
||||
|
||||
virtual void vertex_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) = 0;
|
||||
virtual void geometry_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) = 0;
|
||||
virtual void fragment_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) = 0;
|
||||
virtual void compute_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) = 0;
|
||||
virtual bool finalize(const shader::ShaderCreateInfo *info = nullptr) = 0;
|
||||
/* Pre-warms PSOs using parent shader's cached PSO descriptors. Limit specifies maximum PSOs to
|
||||
* warm. If -1, compiles all PSO permutations in parent shader.
|
||||
*
|
||||
* See `GPU_shader_warm_cache(..)` in `GPU_shader.hh` for more information. */
|
||||
virtual void warm_cache(int limit) = 0;
|
||||
|
||||
virtual void bind(const shader::SpecializationConstants *constants_state) = 0;
|
||||
virtual void unbind() = 0;
|
||||
|
||||
virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0;
|
||||
virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0;
|
||||
|
||||
/* Add specialization constant declarations to shader instance. */
|
||||
void specialization_constants_init(const shader::ShaderCreateInfo &info);
|
||||
|
||||
static std::string defines_declare(const shader::ShaderCreateInfo &info);
|
||||
virtual std::string resources_declare(const shader::ShaderCreateInfo &info) const = 0;
|
||||
virtual std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
|
||||
virtual std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
|
||||
virtual std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
|
||||
virtual std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const = 0;
|
||||
virtual std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const = 0;
|
||||
|
||||
StringRefNull name_get() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
void parent_set(Shader *parent)
|
||||
{
|
||||
parent_shader_ = parent;
|
||||
}
|
||||
|
||||
Shader *parent_get() const
|
||||
{
|
||||
return parent_shader_;
|
||||
}
|
||||
|
||||
static void set_scene_linear_to_xyz_uniform(gpu::Shader *shader);
|
||||
static void set_srgb_uniform(Context *ctx, gpu::Shader *shader);
|
||||
static void set_framebuffer_srgb_target(int use_srgb_to_linear);
|
||||
|
||||
static void dump_source_to_disk(StringRef shader_name,
|
||||
StringRef shader_name_with_stage_name,
|
||||
StringRef extension,
|
||||
StringRef source);
|
||||
|
||||
static std::string run_preprocessor(StringRef source, bool no_dead_code_elimination);
|
||||
|
||||
protected:
|
||||
void print_log(Span<StringRefNull> sources,
|
||||
const char *log,
|
||||
const char *stage,
|
||||
bool error,
|
||||
GPULogParser *parser);
|
||||
};
|
||||
|
||||
class ShaderCompiler {
|
||||
struct Sources {
|
||||
std::string vert;
|
||||
std::string geom;
|
||||
std::string frag;
|
||||
std::string comp;
|
||||
};
|
||||
|
||||
struct AsyncCompilation;
|
||||
struct ParallelWork {
|
||||
ShaderCompiler *compiler = nullptr;
|
||||
ShaderCompiler::AsyncCompilation *compilation = nullptr;
|
||||
WorkID id = 0;
|
||||
};
|
||||
|
||||
struct AsyncCompilation {
|
||||
Shader *shader = nullptr;
|
||||
const shader::ShaderCreateInfo *info = nullptr;
|
||||
|
||||
std::unique_ptr<ShaderSpecialization> specialization = nullptr;
|
||||
|
||||
std::unique_ptr<ParallelWork> work;
|
||||
|
||||
std::atomic<bool> is_ready = false;
|
||||
|
||||
bool is_specialization()
|
||||
{
|
||||
return specialization != nullptr;
|
||||
}
|
||||
};
|
||||
Map<AsyncCompilationHandle, AsyncCompilation *> async_compilations_;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable compilation_finished_notification_;
|
||||
|
||||
std::unique_ptr<GPUWorker> compilation_worker_;
|
||||
|
||||
bool support_specializations_;
|
||||
|
||||
static void do_work_static_cb(void *payload);
|
||||
void do_work(ParallelWork &work);
|
||||
|
||||
AsyncCompilationHandle next_handle_ = 1;
|
||||
|
||||
bool is_compiling_impl();
|
||||
|
||||
bool is_paused_ = false;
|
||||
std::condition_variable pause_finished_notification_;
|
||||
|
||||
protected:
|
||||
/* Must be called earlier from the destructor of the subclass if the compilation process relies
|
||||
* on subclass resources. */
|
||||
void destruct_compilation_worker()
|
||||
{
|
||||
compilation_worker_.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
ShaderCompiler(uint32_t threads_count = 1,
|
||||
GPUWorker::ContextType context_type = GPUWorker::ContextType::PerThread,
|
||||
bool support_specializations = false);
|
||||
virtual ~ShaderCompiler();
|
||||
|
||||
Shader *compile(const shader::ShaderCreateInfo &info, bool is_codegen_only);
|
||||
|
||||
virtual Shader *compile_shader(const shader::ShaderCreateInfo &info);
|
||||
virtual void specialize_shader(const ShaderSpecialization & /*specialization*/) {};
|
||||
|
||||
AsyncCompilationHandle async_compilation(const shader::ShaderCreateInfo *info,
|
||||
CompilationPriority priority);
|
||||
void asyc_compilation_cancel(AsyncCompilationHandle &handle);
|
||||
bool async_compilation_is_ready(AsyncCompilationHandle handle);
|
||||
Shader *async_compilation_finalize(AsyncCompilationHandle &handle);
|
||||
|
||||
AsyncSpecializationHandle async_specialization(const ShaderSpecialization &specialization,
|
||||
CompilationPriority priority);
|
||||
bool async_specialization_is_ready(AsyncSpecializationHandle &handle);
|
||||
|
||||
bool is_compiling();
|
||||
void wait_for_all();
|
||||
void pause_all();
|
||||
void continue_all();
|
||||
};
|
||||
|
||||
enum class Severity {
|
||||
Unknown,
|
||||
Warning,
|
||||
Error,
|
||||
Note,
|
||||
};
|
||||
|
||||
struct LogCursor {
|
||||
int source = -1;
|
||||
int row = -1;
|
||||
int column = -1;
|
||||
std::string file_name_and_error_line;
|
||||
};
|
||||
|
||||
struct GPULogItem {
|
||||
LogCursor cursor;
|
||||
Severity severity = Severity::Unknown;
|
||||
};
|
||||
|
||||
class GPULogParser {
|
||||
public:
|
||||
virtual const char *parse_line(const char *source_combined,
|
||||
const char *log_line,
|
||||
GPULogItem &log_item) = 0;
|
||||
|
||||
protected:
|
||||
const char *skip_severity(const char *log_line,
|
||||
GPULogItem &log_item,
|
||||
const char *error_msg,
|
||||
const char *warning_msg,
|
||||
const char *note_msg) const;
|
||||
const char *skip_separators(const char *log_line, const StringRef separators) const;
|
||||
const char *skip_until(const char *log_line, char stop_char) const;
|
||||
bool at_number(const char *log_line) const;
|
||||
bool at_any(const char *log_line, const StringRef chars) const;
|
||||
int parse_number(const char *log_line, const char **r_new_position) const;
|
||||
|
||||
static size_t line_start_get(StringRefNull source_combined, size_t target_line);
|
||||
static StringRef filename_get(StringRefNull source_combined, size_t pos);
|
||||
static size_t source_line_get(StringRefNull source_combined, size_t pos);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GPULogParser");
|
||||
};
|
||||
|
||||
void printf_begin(Context *ctx);
|
||||
void printf_end(Context *ctx);
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/* XXX do not use it. Special hack to use OCIO with batch API. */
|
||||
gpu::Shader *immGetShader();
|
||||
|
||||
} // namespace blender
|
||||
370
blender-5.2.0/source/blender/gpu/intern/gpu_state.cc
Normal file
370
blender-5.2.0/source/blender/gpu/intern/gpu_state.cc
Normal file
@@ -0,0 +1,370 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#ifndef GPU_STANDALONE
|
||||
# include "DNA_userdef_types.h"
|
||||
# define PIXELSIZE (U.pixelsize)
|
||||
#else
|
||||
# define PIXELSIZE (1.0f)
|
||||
#endif
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "gpu_state_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
#define SET_STATE(_prefix, _state, _value) \
|
||||
do { \
|
||||
StateManager *stack = Context::get()->state_manager; \
|
||||
auto &state_object = stack->_prefix##state; \
|
||||
state_object._state = (_value); \
|
||||
} while (0)
|
||||
|
||||
#define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value)
|
||||
#define SET_MUTABLE_STATE(_state, _value) SET_STATE(mutable_, _state, _value)
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Immutable state Setters
|
||||
* \{ */
|
||||
|
||||
void GPU_blend(GPUBlend blend)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(blend, blend);
|
||||
}
|
||||
|
||||
void GPU_face_culling(GPUFaceCullTest culling)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(culling_test, culling);
|
||||
}
|
||||
|
||||
GPUFaceCullTest GPU_face_culling_get()
|
||||
{
|
||||
GPUState &state = Context::get()->state_manager->state;
|
||||
return GPUFaceCullTest(state.culling_test);
|
||||
}
|
||||
|
||||
void GPU_front_facing(bool invert)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(invert_facing, invert);
|
||||
}
|
||||
|
||||
void GPU_provoking_vertex(GPUProvokingVertex vert)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(provoking_vert, vert);
|
||||
}
|
||||
|
||||
void GPU_depth_test(GPUDepthTest test)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(depth_test, test);
|
||||
}
|
||||
|
||||
void GPU_stencil_test(GPUStencilTest test)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(stencil_test, test);
|
||||
}
|
||||
|
||||
void GPU_line_smooth(bool enable)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(line_smooth, enable);
|
||||
}
|
||||
|
||||
void GPU_polygon_smooth(bool enable)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(polygon_smooth, enable);
|
||||
}
|
||||
|
||||
void GPU_logic_op_xor_set(bool enable)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(logic_op_xor, enable);
|
||||
}
|
||||
|
||||
void GPU_write_mask(GPUWriteMask mask)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(write_mask, mask);
|
||||
}
|
||||
|
||||
void GPU_color_mask(bool r, bool g, bool b, bool a)
|
||||
{
|
||||
StateManager *stack = Context::get()->state_manager;
|
||||
auto &state = stack->state;
|
||||
uint32_t write_mask = state.write_mask;
|
||||
SET_FLAG_FROM_TEST(write_mask, r, uint32_t(GPU_WRITE_RED));
|
||||
SET_FLAG_FROM_TEST(write_mask, g, uint32_t(GPU_WRITE_GREEN));
|
||||
SET_FLAG_FROM_TEST(write_mask, b, uint32_t(GPU_WRITE_BLUE));
|
||||
SET_FLAG_FROM_TEST(write_mask, a, uint32_t(GPU_WRITE_ALPHA));
|
||||
state.write_mask = write_mask;
|
||||
}
|
||||
|
||||
void GPU_depth_mask(bool depth)
|
||||
{
|
||||
StateManager *stack = Context::get()->state_manager;
|
||||
auto &state = stack->state;
|
||||
uint32_t write_mask = state.write_mask;
|
||||
SET_FLAG_FROM_TEST(write_mask, depth, uint32_t(GPU_WRITE_DEPTH));
|
||||
state.write_mask = write_mask;
|
||||
}
|
||||
|
||||
void GPU_clip_distances(int distances_enabled)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(clip_distances, distances_enabled);
|
||||
}
|
||||
|
||||
void GPU_state_set(GPUWriteMask write_mask,
|
||||
GPUBlend blend,
|
||||
GPUFaceCullTest culling_test,
|
||||
GPUDepthTest depth_test,
|
||||
GPUStencilTest stencil_test,
|
||||
GPUStencilOp stencil_op,
|
||||
GPUProvokingVertex provoking_vert)
|
||||
{
|
||||
StateManager *stack = Context::get()->state_manager;
|
||||
auto &state = stack->state;
|
||||
state.write_mask = uint32_t(write_mask);
|
||||
state.blend = uint32_t(blend);
|
||||
state.culling_test = uint32_t(culling_test);
|
||||
state.depth_test = uint32_t(depth_test);
|
||||
state.stencil_test = uint32_t(stencil_test);
|
||||
state.stencil_op = uint32_t(stencil_op);
|
||||
state.provoking_vert = uint32_t(provoking_vert);
|
||||
}
|
||||
|
||||
void GPU_clip_control_unit_range(bool enable)
|
||||
{
|
||||
SET_IMMUTABLE_STATE(clip_control, enable);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mutable State Setters
|
||||
* \{ */
|
||||
|
||||
void GPU_line_width(float width)
|
||||
{
|
||||
width = max_ff(1.0f, width * PIXELSIZE);
|
||||
SET_MUTABLE_STATE(line_width, width);
|
||||
}
|
||||
|
||||
void GPU_point_size(float size)
|
||||
{
|
||||
StateManager *stack = Context::get()->state_manager;
|
||||
auto &state = stack->mutable_state;
|
||||
/* Keep the sign of point_size since it represents the enable state. */
|
||||
state.point_size = size * ((state.point_size > 0.0) ? 1.0f : -1.0f);
|
||||
}
|
||||
|
||||
void GPU_program_point_size(bool enable)
|
||||
{
|
||||
StateManager *stack = Context::get()->state_manager;
|
||||
auto &state = stack->mutable_state;
|
||||
/* Set point size sign negative to disable. */
|
||||
state.point_size = fabsf(state.point_size) * (enable ? 1 : -1);
|
||||
}
|
||||
|
||||
void GPU_scissor_test(bool enable)
|
||||
{
|
||||
Context::get()->active_fb->scissor_test_set(enable);
|
||||
}
|
||||
|
||||
void GPU_scissor(int x, int y, int width, int height)
|
||||
{
|
||||
int scissor_rect[4] = {x, y, width, height};
|
||||
Context::get()->active_fb->scissor_set(scissor_rect);
|
||||
}
|
||||
|
||||
void GPU_viewport(int x, int y, int width, int height)
|
||||
{
|
||||
int viewport_rect[4] = {x, y, width, height};
|
||||
Context::get()->active_fb->viewport_set(viewport_rect);
|
||||
}
|
||||
|
||||
void GPU_stencil_reference_set(uint reference)
|
||||
{
|
||||
SET_MUTABLE_STATE(stencil_reference, uint8_t(reference));
|
||||
}
|
||||
|
||||
void GPU_stencil_write_mask_set(uint write_mask)
|
||||
{
|
||||
SET_MUTABLE_STATE(stencil_write_mask, uint8_t(write_mask));
|
||||
}
|
||||
|
||||
void GPU_stencil_compare_mask_set(uint compare_mask)
|
||||
{
|
||||
SET_MUTABLE_STATE(stencil_compare_mask, uint8_t(compare_mask));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name State Getters
|
||||
* \{ */
|
||||
|
||||
GPUBlend GPU_blend_get()
|
||||
{
|
||||
GPUState &state = Context::get()->state_manager->state;
|
||||
return GPUBlend(state.blend);
|
||||
}
|
||||
|
||||
GPUWriteMask GPU_write_mask_get()
|
||||
{
|
||||
GPUState &state = Context::get()->state_manager->state;
|
||||
return GPUWriteMask(state.write_mask);
|
||||
}
|
||||
|
||||
uint GPU_stencil_mask_get()
|
||||
{
|
||||
const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
|
||||
return state.stencil_write_mask;
|
||||
}
|
||||
|
||||
GPUDepthTest GPU_depth_test_get()
|
||||
{
|
||||
GPUState &state = Context::get()->state_manager->state;
|
||||
return GPUDepthTest(state.depth_test);
|
||||
}
|
||||
|
||||
GPUStencilTest GPU_stencil_test_get()
|
||||
{
|
||||
GPUState &state = Context::get()->state_manager->state;
|
||||
return GPUStencilTest(state.stencil_test);
|
||||
}
|
||||
|
||||
float GPU_line_width_get()
|
||||
{
|
||||
const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
|
||||
return state.line_width;
|
||||
}
|
||||
|
||||
bool GPU_line_smooth_get()
|
||||
{
|
||||
const GPUState &state = Context::get()->state_manager->state;
|
||||
return bool(state.line_smooth);
|
||||
}
|
||||
|
||||
void GPU_scissor_get(int coords[4])
|
||||
{
|
||||
Context::get()->active_fb->scissor_get(coords);
|
||||
}
|
||||
|
||||
void GPU_viewport_size_get_f(float coords[4])
|
||||
{
|
||||
int viewport[4];
|
||||
Context::get()->active_fb->viewport_get(viewport);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
coords[i] = viewport[i];
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_viewport_size_get_i(int coords[4])
|
||||
{
|
||||
Context::get()->active_fb->viewport_get(coords);
|
||||
}
|
||||
|
||||
bool GPU_depth_mask_get()
|
||||
{
|
||||
const GPUState &state = Context::get()->state_manager->state;
|
||||
return (state.write_mask & GPU_WRITE_DEPTH) != 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Context Utils
|
||||
* \{ */
|
||||
|
||||
void GPU_flush()
|
||||
{
|
||||
Context::get()->flush();
|
||||
}
|
||||
|
||||
void GPU_finish()
|
||||
{
|
||||
Context::get()->finish();
|
||||
}
|
||||
|
||||
void GPU_apply_state()
|
||||
{
|
||||
Context::get()->state_manager->apply_state();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Synchronization Utils
|
||||
* \{ */
|
||||
|
||||
void GPU_memory_barrier(GPUBarrier barrier)
|
||||
{
|
||||
Context::get()->state_manager->issue_barrier(barrier);
|
||||
}
|
||||
|
||||
GPUFence *GPU_fence_create()
|
||||
{
|
||||
Fence *fence = GPUBackend::get()->fence_alloc();
|
||||
return wrap(fence);
|
||||
}
|
||||
|
||||
void GPU_fence_free(GPUFence *fence)
|
||||
{
|
||||
delete unwrap(fence);
|
||||
}
|
||||
|
||||
void GPU_fence_signal(GPUFence *fence)
|
||||
{
|
||||
unwrap(fence)->signal();
|
||||
}
|
||||
|
||||
void GPU_fence_wait(GPUFence *fence)
|
||||
{
|
||||
unwrap(fence)->wait();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Default State
|
||||
* \{ */
|
||||
|
||||
StateManager::StateManager()
|
||||
{
|
||||
/* Set default state. */
|
||||
state.write_mask = GPU_WRITE_COLOR;
|
||||
state.blend = GPU_BLEND_NONE;
|
||||
state.culling_test = GPU_CULL_NONE;
|
||||
state.depth_test = GPU_DEPTH_NONE;
|
||||
state.stencil_test = GPU_STENCIL_NONE;
|
||||
state.stencil_op = GPU_STENCIL_OP_NONE;
|
||||
state.provoking_vert = GPU_VERTEX_LAST;
|
||||
state.logic_op_xor = false;
|
||||
state.invert_facing = false;
|
||||
state.clip_distances = 0;
|
||||
state.clip_control = false;
|
||||
state.polygon_smooth = false;
|
||||
state.line_smooth = false;
|
||||
|
||||
mutable_state.point_size = -1.0f; /* Negative is not using point size. */
|
||||
mutable_state.line_width = 1.0f;
|
||||
mutable_state.stencil_write_mask = 0x00;
|
||||
mutable_state.stencil_compare_mask = 0x00;
|
||||
mutable_state.stencil_reference = 0x00;
|
||||
|
||||
image_formats.fill(TextureWriteFormat::Invalid);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
190
blender-5.2.0/source/blender/gpu/intern/gpu_state_private.hh
Normal file
190
blender-5.2.0/source/blender/gpu/intern/gpu_state_private.hh
Normal file
@@ -0,0 +1,190 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* Encapsulate all pipeline state that we need to track.
|
||||
* Try to keep small to reduce validation time. */
|
||||
union GPUState {
|
||||
struct {
|
||||
/** GPUWriteMask */
|
||||
uint32_t write_mask : 13;
|
||||
/** GPUBlend */
|
||||
uint32_t blend : 4;
|
||||
/** GPUFaceCullTest */
|
||||
uint32_t culling_test : 2;
|
||||
/** GPUDepthTest */
|
||||
uint32_t depth_test : 3;
|
||||
/** GPUStencilTest */
|
||||
uint32_t stencil_test : 3;
|
||||
/** GPUStencilOp */
|
||||
uint32_t stencil_op : 3;
|
||||
/** GPUProvokingVertex */
|
||||
uint32_t provoking_vert : 1;
|
||||
/** Enable bits. */
|
||||
uint32_t logic_op_xor : 1;
|
||||
uint32_t invert_facing : 1;
|
||||
/** Clip range of 0..1 on OpenGL. */
|
||||
uint32_t clip_control : 1;
|
||||
/** Number of clip distances enabled. */
|
||||
/* TODO(fclem): This should be a shader property. */
|
||||
uint32_t clip_distances : 3;
|
||||
/* TODO(fclem): remove, old opengl features. */
|
||||
uint32_t polygon_smooth : 1;
|
||||
uint32_t line_smooth : 1;
|
||||
};
|
||||
/* Here to allow fast bit-wise ops. */
|
||||
uint64_t data;
|
||||
};
|
||||
|
||||
BLI_STATIC_ASSERT(sizeof(GPUState) == sizeof(uint64_t), "GPUState is too big.");
|
||||
|
||||
inline bool operator==(const GPUState &a, const GPUState &b)
|
||||
{
|
||||
return a.data == b.data;
|
||||
}
|
||||
|
||||
inline bool operator!=(const GPUState &a, const GPUState &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline GPUState operator^(const GPUState &a, const GPUState &b)
|
||||
{
|
||||
GPUState r;
|
||||
r.data = a.data ^ b.data;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline GPUState operator~(const GPUState &a)
|
||||
{
|
||||
GPUState r;
|
||||
r.data = ~a.data;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Mutable state that does not require pipeline change. */
|
||||
union GPUStateMutable {
|
||||
struct {
|
||||
/* Viewport State */
|
||||
/** Positive if using program point size. */
|
||||
/* TODO(fclem): should be passed as uniform to all shaders. */
|
||||
float point_size;
|
||||
/** Not supported on every platform. Prefer using wide-line shader. */
|
||||
float line_width;
|
||||
/** Mutable stencil states. */
|
||||
uint8_t stencil_write_mask;
|
||||
uint8_t stencil_compare_mask;
|
||||
uint8_t stencil_reference;
|
||||
uint8_t _pad0[5];
|
||||
/* IMPORTANT: ensure x64 struct alignment. */
|
||||
};
|
||||
/* Here to allow fast bit-wise ops. */
|
||||
uint64_t data[2];
|
||||
};
|
||||
|
||||
BLI_STATIC_ASSERT(sizeof(GPUStateMutable) == sizeof(GPUStateMutable::data),
|
||||
"GPUStateMutable is too big.");
|
||||
|
||||
inline bool operator==(const GPUStateMutable &a, const GPUStateMutable &b)
|
||||
{
|
||||
return a.data[0] == b.data[0] && a.data[1] == b.data[1];
|
||||
}
|
||||
|
||||
inline bool operator!=(const GPUStateMutable &a, const GPUStateMutable &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline GPUStateMutable operator^(const GPUStateMutable &a, const GPUStateMutable &b)
|
||||
{
|
||||
GPUStateMutable r;
|
||||
for (int i = 0; i < ARRAY_SIZE(a.data); i++) {
|
||||
r.data[i] = a.data[i] ^ b.data[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
inline GPUStateMutable operator~(const GPUStateMutable &a)
|
||||
{
|
||||
GPUStateMutable r;
|
||||
for (int i = 0; i < ARRAY_SIZE(a.data); i++) {
|
||||
r.data[i] = ~a.data[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* State manager keeping track of the draw state and applying it before drawing.
|
||||
* Base class which is then specialized for each implementation (GL, VK, ...).
|
||||
*/
|
||||
class StateManager {
|
||||
public:
|
||||
GPUState state;
|
||||
GPUStateMutable mutable_state;
|
||||
|
||||
/* Formats of all image units. */
|
||||
std::array<TextureWriteFormat, GPU_MAX_IMAGE> image_formats;
|
||||
|
||||
StateManager();
|
||||
virtual ~StateManager() = default;
|
||||
|
||||
virtual void apply_state() = 0;
|
||||
virtual void force_state() = 0;
|
||||
|
||||
virtual void issue_barrier(GPUBarrier barrier_bits) = 0;
|
||||
|
||||
virtual void texture_bind(Texture *tex, GPUSamplerState sampler, int unit) = 0;
|
||||
virtual void texture_unbind(Texture *tex) = 0;
|
||||
virtual void texture_unbind_all() = 0;
|
||||
|
||||
virtual void image_bind(Texture *tex, int unit) = 0;
|
||||
virtual void image_unbind(Texture *tex) = 0;
|
||||
virtual void image_unbind_all() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* GPUFence.
|
||||
*/
|
||||
class Fence {
|
||||
protected:
|
||||
bool signalled_ = false;
|
||||
|
||||
public:
|
||||
Fence() = default;
|
||||
virtual ~Fence() = default;
|
||||
|
||||
virtual void signal() = 0;
|
||||
virtual void wait() = 0;
|
||||
};
|
||||
|
||||
/* Syntactic sugar. */
|
||||
static inline GPUFence *wrap(Fence *fence)
|
||||
{
|
||||
return reinterpret_cast<GPUFence *>(fence);
|
||||
}
|
||||
static inline Fence *unwrap(GPUFence *fence)
|
||||
{
|
||||
return reinterpret_cast<Fence *>(fence);
|
||||
}
|
||||
static inline const Fence *unwrap(const GPUFence *fence)
|
||||
{
|
||||
return reinterpret_cast<const Fence *>(fence);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
142
blender-5.2.0/source/blender/gpu/intern/gpu_storage_buffer.cc
Normal file
142
blender-5.2.0/source/blender/gpu/intern/gpu_storage_buffer.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
|
||||
#include "GPU_storage_buffer.hh"
|
||||
#include "GPU_vertex_buffer.hh" /* For GPUUsageType. */
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_storage_buffer_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
namespace gpu {
|
||||
|
||||
StorageBuf::StorageBuf(size_t size, const char *name)
|
||||
{
|
||||
size_in_bytes_ = usage_size_in_bytes_ = size;
|
||||
STRNCPY(name_, name);
|
||||
}
|
||||
|
||||
StorageBuf::~StorageBuf()
|
||||
{
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
|
||||
void StorageBuf::usage_size_set(size_t usage_size)
|
||||
{
|
||||
BLI_assert(usage_size <= size_in_bytes_);
|
||||
usage_size_in_bytes_ = usage_size;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name C-API
|
||||
* \{ */
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
gpu::StorageBuf *GPU_storagebuf_create_ex(size_t size,
|
||||
const void *data,
|
||||
GPUUsageType usage,
|
||||
const char *name)
|
||||
{
|
||||
StorageBuf *ssbo = GPUBackend::get()->storagebuf_alloc(size, usage, name);
|
||||
/* Direct init. */
|
||||
if (data != nullptr) {
|
||||
ssbo->update(data);
|
||||
}
|
||||
else if (G.debug & G_DEBUG_GPU) {
|
||||
/* Fill the buffer with poison values.
|
||||
* (NaN for floats, -1 for `int` and "max value" for `uint`). */
|
||||
Vector<uchar> uninitialized_data(size, 0xFF);
|
||||
ssbo->update(uninitialized_data.data());
|
||||
}
|
||||
|
||||
return ssbo;
|
||||
}
|
||||
|
||||
void GPU_storagebuf_free(gpu::StorageBuf *ssbo)
|
||||
{
|
||||
delete ssbo;
|
||||
}
|
||||
|
||||
void GPU_storagebuf_usage_size_set(gpu::StorageBuf *ssbo, size_t usage_size)
|
||||
{
|
||||
ssbo->usage_size_set(usage_size);
|
||||
}
|
||||
|
||||
void GPU_storagebuf_update(gpu::StorageBuf *ssbo, const void *data)
|
||||
{
|
||||
ssbo->update(data);
|
||||
}
|
||||
|
||||
void GPU_storagebuf_bind(gpu::StorageBuf *ssbo, int slot)
|
||||
{
|
||||
ssbo->bind(slot);
|
||||
}
|
||||
|
||||
void GPU_storagebuf_unbind(gpu::StorageBuf *ssbo)
|
||||
{
|
||||
ssbo->unbind();
|
||||
}
|
||||
|
||||
void GPU_storagebuf_debug_unbind_all()
|
||||
{
|
||||
Context::get()->debug_unbind_all_ssbo();
|
||||
}
|
||||
|
||||
void GPU_storagebuf_clear_to_zero(gpu::StorageBuf *ssbo)
|
||||
{
|
||||
GPU_storagebuf_clear(ssbo, 0);
|
||||
}
|
||||
|
||||
void GPU_storagebuf_clear(gpu::StorageBuf *ssbo, uint32_t clear_value)
|
||||
{
|
||||
ssbo->clear(clear_value);
|
||||
}
|
||||
|
||||
void GPU_storagebuf_copy_sub_from_vertbuf(
|
||||
gpu::StorageBuf *ssbo, gpu::VertBuf *src, uint dst_offset, uint src_offset, uint copy_size)
|
||||
{
|
||||
ssbo->copy_sub(src, dst_offset, src_offset, copy_size);
|
||||
}
|
||||
|
||||
void GPU_storagebuf_sync_to_host(gpu::StorageBuf *ssbo)
|
||||
{
|
||||
ssbo->async_flush_to_host();
|
||||
}
|
||||
|
||||
void GPU_storagebuf_read(gpu::StorageBuf *ssbo, void *data)
|
||||
{
|
||||
ssbo->read(data);
|
||||
}
|
||||
|
||||
void GPU_storagebuf_sync_as_indirect_buffer(gpu::StorageBuf *ssbo)
|
||||
{
|
||||
ssbo->sync_as_indirect_buffer();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user