Use shared_from_this in GlTextureBuffer::GetReadView, GetWriteView
This ensures that the callbacks in GlTextureView won't call an expired object, even if user code holds a GlTextureView after releasing the buffer. Note that GlTextureBuffer is not always held by a shared_ptr, but it always is when GpuBuffer calls GetRead/WriteView on it. An alternative solution would have been to have GpuBuffer pass its shared_ptr<GlTextureBuffer> to the view method, which could have been implemented with some compile-time logic to detect whether the method expects such an argument. However, that doesn't seem necessary. PiperOrigin-RevId: 489611843
This commit is contained in:
		
							parent
							
								
									bbd5da7971
								
							
						
					
					
						commit
						eb8ef1ace0
					
				| 
						 | 
				
			
			@ -260,13 +260,18 @@ GlTextureView GlTextureBuffer::GetReadView(internal::types<GlTextureView>,
 | 
			
		|||
  auto gl_context = GlContext::GetCurrent();
 | 
			
		||||
  CHECK(gl_context);
 | 
			
		||||
  CHECK_EQ(plane, 0);
 | 
			
		||||
  // Note that this method is only supposed to be called by GpuBuffer, which
 | 
			
		||||
  // ensures this condition is satisfied.
 | 
			
		||||
  DCHECK(!weak_from_this().expired())
 | 
			
		||||
      << "GlTextureBuffer must be held in shared_ptr to get a GlTextureView";
 | 
			
		||||
  // Insert wait call to sync with the producer.
 | 
			
		||||
  WaitOnGpu();
 | 
			
		||||
  GlTextureView::DetachFn detach = [this](GlTextureView& texture) {
 | 
			
		||||
    // Inform the GlTextureBuffer that we have finished accessing its
 | 
			
		||||
    // contents, and create a consumer sync point.
 | 
			
		||||
    DidRead(texture.gl_context()->CreateSyncToken());
 | 
			
		||||
  };
 | 
			
		||||
  GlTextureView::DetachFn detach =
 | 
			
		||||
      [texbuf = shared_from_this()](GlTextureView& texture) {
 | 
			
		||||
        // Inform the GlTextureBuffer that we have finished accessing its
 | 
			
		||||
        // contents, and create a consumer sync point.
 | 
			
		||||
        texbuf->DidRead(texture.gl_context()->CreateSyncToken());
 | 
			
		||||
      };
 | 
			
		||||
  return GlTextureView(gl_context.get(), target(), name(), width(), height(),
 | 
			
		||||
                       plane, std::move(detach), nullptr);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -276,12 +281,18 @@ GlTextureView GlTextureBuffer::GetWriteView(internal::types<GlTextureView>,
 | 
			
		|||
  auto gl_context = GlContext::GetCurrent();
 | 
			
		||||
  CHECK(gl_context);
 | 
			
		||||
  CHECK_EQ(plane, 0);
 | 
			
		||||
  // Note that this method is only supposed to be called by GpuBuffer, which
 | 
			
		||||
  // ensures this condition is satisfied.
 | 
			
		||||
  DCHECK(!weak_from_this().expired())
 | 
			
		||||
      << "GlTextureBuffer must be held in shared_ptr to get a GlTextureView";
 | 
			
		||||
  // Insert wait call to sync with the producer.
 | 
			
		||||
  WaitOnGpu();
 | 
			
		||||
  Reuse();  // TODO: the producer wait should probably be part of Reuse in the
 | 
			
		||||
            // case when there are no consumers.
 | 
			
		||||
  GlTextureView::DoneWritingFn done_writing =
 | 
			
		||||
      [this](const GlTextureView& texture) { ViewDoneWriting(texture); };
 | 
			
		||||
      [texbuf = shared_from_this()](const GlTextureView& texture) {
 | 
			
		||||
        texbuf->ViewDoneWriting(texture);
 | 
			
		||||
      };
 | 
			
		||||
  return GlTextureView(gl_context.get(), target(), name(), width(), height(),
 | 
			
		||||
                       plane, nullptr, std::move(done_writing));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,8 @@ class GlCalculatorHelperImpl;
 | 
			
		|||
// Implements a GPU memory buffer as an OpenGL texture. For internal use.
 | 
			
		||||
class GlTextureBuffer
 | 
			
		||||
    : public internal::GpuBufferStorageImpl<
 | 
			
		||||
          GlTextureBuffer, internal::ViewProvider<GlTextureView>> {
 | 
			
		||||
          GlTextureBuffer, internal::ViewProvider<GlTextureView>>,
 | 
			
		||||
      public std::enable_shared_from_this<GlTextureBuffer> {
 | 
			
		||||
 public:
 | 
			
		||||
  // This is called when the texture buffer is deleted. It is passed a sync
 | 
			
		||||
  // token created at that time on the GlContext. If the GlTextureBuffer has
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,8 @@
 | 
			
		|||
 | 
			
		||||
#include "mediapipe/gpu/gpu_buffer.h"
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "mediapipe/framework/formats/image_format.pb.h"
 | 
			
		||||
#include "mediapipe/framework/port/gmock.h"
 | 
			
		||||
#include "mediapipe/framework/port/gtest.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -206,5 +208,25 @@ TEST_F(GpuBufferTest, Overwrite) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(GpuBufferTest, GlTextureViewRetainsWhatItNeeds) {
 | 
			
		||||
  GpuBuffer buffer(300, 200, GpuBufferFormat::kBGRA32);
 | 
			
		||||
  {
 | 
			
		||||
    std::shared_ptr<ImageFrame> view = buffer.GetWriteView<ImageFrame>();
 | 
			
		||||
    EXPECT_EQ(view->Width(), 300);
 | 
			
		||||
    EXPECT_EQ(view->Height(), 200);
 | 
			
		||||
    FillImageFrameRGBA(*view, 255, 0, 0, 255);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  RunInGlContext([buffer = std::move(buffer)]() mutable {
 | 
			
		||||
    // This is not a recommended pattern, but let's make sure that we don't
 | 
			
		||||
    // crash if the buffer is released before the view. The view can hold
 | 
			
		||||
    // callbacks into its underlying storage.
 | 
			
		||||
    auto view = buffer.GetReadView<GlTextureView>(0);
 | 
			
		||||
    buffer = nullptr;
 | 
			
		||||
  });
 | 
			
		||||
  // We're really checking that we haven't crashed.
 | 
			
		||||
  EXPECT_TRUE(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // anonymous namespace
 | 
			
		||||
}  // namespace mediapipe
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user