diff options
Diffstat (limited to 'source/blender/gpu/metal/mtl_context.mm')
-rw-r--r-- | source/blender/gpu/metal/mtl_context.mm | 229 |
1 files changed, 206 insertions, 23 deletions
diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index 94f5682b11b..64a44c6f718 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -22,7 +22,7 @@ namespace blender::gpu { bool MTLTemporaryBufferRange::requires_flush() { - /* We do not need to flush shared memory */ + /* We do not need to flush shared memory. */ return this->options & MTLResourceStorageModeManaged; } @@ -49,15 +49,86 @@ MTLContext::MTLContext(void *ghost_window) /* Init debug. */ debug::mtl_debug_init(); + /* Initialise command buffer state. */ + this->main_command_buffer.prepare(this); + + /* Frame management. */ + is_inside_frame_ = false; + + /* Create FrameBuffer handles. */ + MTLFrameBuffer *mtl_front_left = new MTLFrameBuffer(this, "front_left"); + MTLFrameBuffer *mtl_back_left = new MTLFrameBuffer(this, "back_left"); + this->front_left = mtl_front_left; + this->back_left = mtl_back_left; + this->active_fb = this->back_left; + /* Prepare platform and capabilities. (Note: With METAL, this needs to be done after CTX + * initialisation). */ + MTLBackend::platform_init(this); + MTLBackend::capabilities_init(this); /* Initialize Metal modules. */ this->state_manager = new MTLStateManager(this); - /* TODO(Metal): Implement. */ + /* Initialise texture read/update structures. */ + this->get_texture_utils().init(); + + /* Bound Samplers struct. */ + for (int i = 0; i < MTL_MAX_TEXTURE_SLOTS; i++) { + samplers_.mtl_sampler[i] = nil; + samplers_.mtl_sampler_flags[i] = DEFAULT_SAMPLER_STATE; + } + + /* Initialise samplers. */ + for (uint i = 0; i < GPU_SAMPLER_MAX; i++) { + MTLSamplerState state; + state.state = static_cast<eGPUSamplerState>(i); + sampler_state_cache_[i] = this->generate_sampler_from_state(state); + } } MTLContext::~MTLContext() { - /* TODO(Metal): Implement. */ + BLI_assert(this == reinterpret_cast<MTLContext *>(GPU_context_active_get())); + /* Ensure rendering is complete command encoders/command buffers are freed. */ + if (MTLBackend::get()->is_inside_render_boundary()) { + this->finish(); + + /* End frame. */ + if (is_inside_frame_) { + this->end_frame(); + } + } + /* Release update/blit shaders. */ + this->get_texture_utils().cleanup(); + + /* Release Sampler States. */ + for (int i = 0; i < GPU_SAMPLER_MAX; i++) { + if (sampler_state_cache_[i] != nil) { + [sampler_state_cache_[i] release]; + sampler_state_cache_[i] = nil; + } + } +} + +void MTLContext::begin_frame() +{ + BLI_assert(MTLBackend::get()->is_inside_render_boundary()); + if (is_inside_frame_) { + return; + } + + /* Begin Command buffer for next frame. */ + is_inside_frame_ = true; +} + +void MTLContext::end_frame() +{ + BLI_assert(is_inside_frame_); + + /* Ensure pre-present work is commited. */ + this->flush(); + + /* Increment frame counter. */ + is_inside_frame_ = false; } void MTLContext::check_error(const char *info) @@ -90,26 +161,83 @@ void MTLContext::memory_statistics_get(int *total_mem, int *free_mem) *free_mem = 0; } -id<MTLCommandBuffer> MTLContext::get_active_command_buffer() +void MTLContext::framebuffer_bind(MTLFrameBuffer *framebuffer) { - /* TODO(Metal): Implement. */ - return nil; + /* We do not yet begin the pass -- We defer beginning the pass until a draw is requested. */ + BLI_assert(framebuffer); + this->active_fb = framebuffer; } -/* Render Pass State and Management */ -void MTLContext::begin_render_pass() +void MTLContext::framebuffer_restore() { - /* TODO(Metal): Implement. */ + /* Bind default framebuffer from context -- + * We defer beginning the pass until a draw is requested. */ + this->active_fb = this->back_left; } -void MTLContext::end_render_pass() + +id<MTLRenderCommandEncoder> MTLContext::ensure_begin_render_pass() { - /* TODO(Metal): Implement. */ + BLI_assert(this); + + /* Ensure the rendering frame has started. */ + if (!is_inside_frame_) { + this->begin_frame(); + } + + /* Check whether a framebuffer is bound. */ + if (!this->active_fb) { + BLI_assert(false && "No framebuffer is bound!"); + return this->main_command_buffer.get_active_render_command_encoder(); + } + + /* Ensure command buffer workload submissions are optimal -- + * Though do not split a batch mid-IMM recording */ + /* TODO(Metal): Add IMM Check once MTLImmediate has been implemented. */ + if (this->main_command_buffer.do_break_submission()/*&& + !((MTLImmediate *)(this->imm))->imm_is_recording()*/) { + this->flush(); + } + + /* Begin pass or perform a pass switch if the active framebuffer has been changed, or if the + * framebuffer state has been modified (is_dirty). */ + if (!this->main_command_buffer.is_inside_render_pass() || + this->active_fb != this->main_command_buffer.get_active_framebuffer() || + this->main_command_buffer.get_active_framebuffer()->get_dirty()) { + + /* Validate bound framebuffer before beginning render pass. */ + if (!static_cast<MTLFrameBuffer *>(this->active_fb)->validate_render_pass()) { + MTL_LOG_WARNING("Framebuffer validation failed, falling back to default framebuffer\n"); + this->framebuffer_restore(); + + if (!static_cast<MTLFrameBuffer *>(this->active_fb)->validate_render_pass()) { + MTL_LOG_ERROR("CRITICAL: DEFAULT FRAMEBUFFER FAIL VALIDATION!!\n"); + } + } + + /* Begin RenderCommandEncoder on main CommandBuffer. */ + bool new_render_pass = false; + id<MTLRenderCommandEncoder> new_enc = + this->main_command_buffer.ensure_begin_render_command_encoder( + static_cast<MTLFrameBuffer *>(this->active_fb), true, &new_render_pass); + if (new_render_pass) { + /* Flag context pipeline state as dirty - dynamic pipeline state need re-applying. */ + this->pipeline_state.dirty_flags = MTL_PIPELINE_STATE_ALL_FLAG; + } + return new_enc; + } + BLI_assert(!this->main_command_buffer.get_active_framebuffer()->get_dirty()); + return this->main_command_buffer.get_active_render_command_encoder(); } -bool MTLContext::is_render_pass_active() +MTLFrameBuffer *MTLContext::get_current_framebuffer() { - /* TODO(Metal): Implement. */ - return false; + MTLFrameBuffer *last_bound = static_cast<MTLFrameBuffer *>(this->active_fb); + return last_bound ? last_bound : this->get_default_framebuffer(); +} + +MTLFrameBuffer *MTLContext::get_default_framebuffer() +{ + return static_cast<MTLFrameBuffer *>(this->back_left); } /** \} */ @@ -200,13 +328,68 @@ void MTLContext::pipeline_state_init() MTLStencilOperationKeep; } +void MTLContext::set_viewport(int origin_x, int origin_y, int width, int height) +{ + BLI_assert(this); + BLI_assert(width > 0); + BLI_assert(height > 0); + BLI_assert(origin_x >= 0); + BLI_assert(origin_y >= 0); + bool changed = (this->pipeline_state.viewport_offset_x != origin_x) || + (this->pipeline_state.viewport_offset_y != origin_y) || + (this->pipeline_state.viewport_width != width) || + (this->pipeline_state.viewport_height != height); + this->pipeline_state.viewport_offset_x = origin_x; + this->pipeline_state.viewport_offset_y = origin_y; + this->pipeline_state.viewport_width = width; + this->pipeline_state.viewport_height = height; + if (changed) { + this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags | + MTL_PIPELINE_STATE_VIEWPORT_FLAG); + } +} + +void MTLContext::set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height) +{ + BLI_assert(this); + bool changed = (this->pipeline_state.scissor_x != scissor_x) || + (this->pipeline_state.scissor_y != scissor_y) || + (this->pipeline_state.scissor_width != scissor_width) || + (this->pipeline_state.scissor_height != scissor_height) || + (this->pipeline_state.scissor_enabled != true); + this->pipeline_state.scissor_x = scissor_x; + this->pipeline_state.scissor_y = scissor_y; + this->pipeline_state.scissor_width = scissor_width; + this->pipeline_state.scissor_height = scissor_height; + this->pipeline_state.scissor_enabled = (scissor_width > 0 && scissor_height > 0); + + if (changed) { + this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags | + MTL_PIPELINE_STATE_SCISSOR_FLAG); + } +} + +void MTLContext::set_scissor_enabled(bool scissor_enabled) +{ + /* Only turn on Scissor if requested scissor region is valid */ + scissor_enabled = scissor_enabled && (this->pipeline_state.scissor_width > 0 && + this->pipeline_state.scissor_height > 0); + + bool changed = (this->pipeline_state.scissor_enabled != scissor_enabled); + this->pipeline_state.scissor_enabled = scissor_enabled; + if (changed) { + this->pipeline_state.dirty_flags = (this->pipeline_state.dirty_flags | + MTL_PIPELINE_STATE_SCISSOR_FLAG); + } +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name Texture State Management * \{ */ -void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit) +void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, uint texture_unit) { BLI_assert(this); BLI_assert(mtl_texture); @@ -226,7 +409,7 @@ void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture mtl_texture->is_bound_ = true; } -void MTLContext::sampler_bind(MTLSamplerState sampler_state, unsigned int sampler_unit) +void MTLContext::sampler_bind(MTLSamplerState sampler_state, uint sampler_unit) { BLI_assert(this); if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() || @@ -271,14 +454,14 @@ void MTLContext::texture_unbind_all() id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state) { - BLI_assert((unsigned int)sampler_state >= 0 && ((unsigned int)sampler_state) < GPU_SAMPLER_MAX); - return this->sampler_state_cache_[(unsigned int)sampler_state]; + BLI_assert((uint)sampler_state >= 0 && ((uint)sampler_state) < GPU_SAMPLER_MAX); + return sampler_state_cache_[(uint)sampler_state]; } id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState sampler_state) { /* Check if sampler already exists for given state. */ - id<MTLSamplerState> st = this->sampler_state_cache_[(unsigned int)sampler_state]; + id<MTLSamplerState> st = sampler_state_cache_[(uint)sampler_state]; if (st != nil) { return st; } @@ -318,7 +501,7 @@ id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState samp descriptor.supportArgumentBuffers = true; id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor]; - this->sampler_state_cache_[(unsigned int)sampler_state] = state; + sampler_state_cache_[(uint)sampler_state] = state; BLI_assert(state != nil); [descriptor autorelease]; @@ -328,10 +511,10 @@ id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState samp id<MTLSamplerState> MTLContext::get_default_sampler_state() { - if (this->default_sampler_state_ == nil) { - this->default_sampler_state_ = this->get_sampler_from_state(DEFAULT_SAMPLER_STATE); + if (default_sampler_state_ == nil) { + default_sampler_state_ = this->get_sampler_from_state(DEFAULT_SAMPLER_STATE); } - return this->default_sampler_state_; + return default_sampler_state_; } /** \} */ |