diff options
Diffstat (limited to 'source/blender/gpu/metal/mtl_context.mm')
-rw-r--r-- | source/blender/gpu/metal/mtl_context.mm | 307 |
1 files changed, 258 insertions, 49 deletions
diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index 94f5682b11b..26cfe6632ef 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -16,48 +16,105 @@ using namespace blender::gpu; namespace blender::gpu { -/* -------------------------------------------------------------------- */ -/** \name Memory Management - * \{ */ - -bool MTLTemporaryBufferRange::requires_flush() -{ - /* We do not need to flush shared memory */ - return this->options & MTLResourceStorageModeManaged; -} - -void MTLTemporaryBufferRange::flush() -{ - if (this->requires_flush()) { - BLI_assert(this->metal_buffer); - BLI_assert((this->buffer_offset + this->size) <= [this->metal_buffer length]); - BLI_assert(this->buffer_offset >= 0); - [this->metal_buffer - didModifyRange:NSMakeRange(this->buffer_offset, this->size - this->buffer_offset)]; - } -} - -/** \} */ +/* Global memory manager. */ +MTLBufferPool MTLContext::global_memory_manager; /* -------------------------------------------------------------------- */ /** \name MTLContext * \{ */ /* Placeholder functions */ -MTLContext::MTLContext(void *ghost_window) +MTLContext::MTLContext(void *ghost_window) : memory_manager(*this), main_command_buffer(*this) { /* Init debug. */ debug::mtl_debug_init(); + /* Initialize command buffer state. */ + this->main_command_buffer.prepare(); + + /* Frame management. */ + is_inside_frame_ = false; + current_frame_index_ = 0; + + /* 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 + * initialization). */ + MTLBackend::platform_init(this); + MTLBackend::capabilities_init(this); + /* Initialize Metal modules. */ + this->memory_manager.init(); this->state_manager = new MTLStateManager(this); - /* TODO(Metal): Implement. */ + /* Ensure global memory manager is initialized. */ + MTLContext::global_memory_manager.init(this->device); + + /* Initialize 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; + } + + /* Initialize 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 (this->get_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 (this->get_inside_frame()) { + return; + } + + /* Begin Command buffer for next frame. */ + is_inside_frame_ = true; +} + +void MTLContext::end_frame() +{ + BLI_assert(this->get_inside_frame()); + + /* Ensure pre-present work is committed. */ + this->flush(); + + /* Increment frame counter. */ + is_inside_frame_ = false; } void MTLContext::check_error(const char *info) @@ -65,20 +122,20 @@ void MTLContext::check_error(const char *info) /* TODO(Metal): Implement. */ } -void MTLContext::activate(void) +void MTLContext::activate() { /* TODO(Metal): Implement. */ } -void MTLContext::deactivate(void) +void MTLContext::deactivate() { /* TODO(Metal): Implement. */ } -void MTLContext::flush(void) +void MTLContext::flush() { /* TODO(Metal): Implement. */ } -void MTLContext::finish(void) +void MTLContext::finish() { /* TODO(Metal): Implement. */ } @@ -90,26 +147,84 @@ 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 (!this->get_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() || + this->is_visibility_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 +315,107 @@ 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 Visibility buffer control for MTLQueryPool. + * \{ */ + +void MTLContext::set_visibility_buffer(gpu::MTLBuffer *buffer) +{ + /* Flag visibility buffer as dirty if the buffer being used for visibility has changed -- + * This is required by the render pass, and we will break the pass if the results destination + * buffer is modified. */ + if (buffer) { + visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_; + visibility_buffer_ = buffer; + visibility_buffer_->debug_ensure_used(); + } + else { + /* If buffer is null, reset visibility state, mark dirty to break render pass if results are no + * longer needed. */ + visibility_is_dirty_ = (visibility_buffer_ != nullptr) || visibility_is_dirty_; + visibility_buffer_ = nullptr; + } +} + +gpu::MTLBuffer *MTLContext::get_visibility_buffer() const +{ + return visibility_buffer_; +} + +void MTLContext::clear_visibility_dirty() +{ + visibility_is_dirty_ = false; +} + +bool MTLContext::is_visibility_dirty() const +{ + return visibility_is_dirty_; +} + /** \} */ /* -------------------------------------------------------------------- */ /** \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 +435,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 +480,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 +527,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 +537,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_; } /** \} */ |