Commit d10b2043 authored by Vasiliy Telezhnikov's avatar Vasiliy Telezhnikov Committed by Commit Bot

Call UpdateTexImage after OnFrameAvailable

Both with SurfaceTexture and AImageReader we get only one FrameAvailable
callback before we call UpdateTexImage. This causes the problem when we
released buffer to codec in early rendering, but dropped the frame
later. In this case we did not called UpdateTexImage for that frame and
will not received OnFrameAvailable for next one causing
WaitForFrameAvailable to timeout.

This CL adds UpdateTexImage call after WaitForFrameAvailable in
RenderToTextureOwnerBackBuffer in case we're already waiting for frame.

Bug: 1113203
Change-Id: I32e703d07df94f7170795077773a30b4578c1a86
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2339495
Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797477}
parent e2c2fc96
...@@ -186,11 +186,11 @@ bool CodecImage::RenderToFrontBuffer() { ...@@ -186,11 +186,11 @@ bool CodecImage::RenderToFrontBuffer() {
return output_buffer_renderer_->RenderToFrontBuffer(); return output_buffer_renderer_->RenderToFrontBuffer();
} }
bool CodecImage::RenderToTextureOwnerBackBuffer(BlockingMode blocking_mode) { bool CodecImage::RenderToTextureOwnerBackBuffer() {
if (!output_buffer_renderer_) if (!output_buffer_renderer_)
return false; return false;
return output_buffer_renderer_->RenderToTextureOwnerBackBuffer(blocking_mode); return output_buffer_renderer_->RenderToTextureOwnerBackBuffer();
} }
bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) { bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) {
......
...@@ -31,8 +31,6 @@ namespace media { ...@@ -31,8 +31,6 @@ namespace media {
class MEDIA_GPU_EXPORT CodecImage class MEDIA_GPU_EXPORT CodecImage
: public gpu::StreamTextureSharedImageInterface { : public gpu::StreamTextureSharedImageInterface {
public: public:
using BlockingMode = CodecOutputBufferRenderer::BlockingMode;
// Callback to notify that a codec image is now unused in the sense of not // Callback to notify that a codec image is now unused in the sense of not
// being out for display. This lets us signal interested folks once a video // being out for display. This lets us signal interested folks once a video
// frame is destroyed and the sync token clears, so that that CodecImage may // frame is destroyed and the sync token clears, so that that CodecImage may
...@@ -134,11 +132,9 @@ class MEDIA_GPU_EXPORT CodecImage ...@@ -134,11 +132,9 @@ class MEDIA_GPU_EXPORT CodecImage
// Renders this image to the back buffer of its texture owner. Only valid if // Renders this image to the back buffer of its texture owner. Only valid if
// is_texture_owner_backed(). Returns true if the buffer is in the back // is_texture_owner_backed(). Returns true if the buffer is in the back
// buffer. Returns false if the buffer was invalidated. // buffer. Returns false if the buffer was invalidated.
// |blocking_mode| indicates whether this should (a) wait for any previously // RenderToTextureOwnerBackBuffer() will not block if there is any previously
// pending rendered frame before rendering this one, or (b) fail if a wait // pending frame and will return false in this case.
// is required. bool RenderToTextureOwnerBackBuffer();
bool RenderToTextureOwnerBackBuffer(
BlockingMode blocking_mode = BlockingMode::kAllowBlocking);
// Release any codec buffer without rendering, if we have one. // Release any codec buffer without rendering, if we have one.
virtual void ReleaseCodecBuffer(); virtual void ReleaseCodecBuffer();
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/android/scoped_hardware_buffer_fence_sync.h" #include "base/android/scoped_hardware_buffer_fence_sync.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/optional.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/texture_manager.h" #include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gl/gl_context.h" #include "ui/gl/gl_context.h"
...@@ -37,6 +38,19 @@ std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded( ...@@ -37,6 +38,19 @@ std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(
return scoped_current; return scoped_current;
} }
class ScopedRestoreTextureBinding {
public:
ScopedRestoreTextureBinding() {
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id_);
}
~ScopedRestoreTextureBinding() {
glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id_);
}
private:
GLint bound_service_id_;
};
} // namespace } // namespace
CodecOutputBufferRenderer::CodecOutputBufferRenderer( CodecOutputBufferRenderer::CodecOutputBufferRenderer(
...@@ -49,8 +63,7 @@ CodecOutputBufferRenderer::CodecOutputBufferRenderer( ...@@ -49,8 +63,7 @@ CodecOutputBufferRenderer::CodecOutputBufferRenderer(
CodecOutputBufferRenderer::~CodecOutputBufferRenderer() = default; CodecOutputBufferRenderer::~CodecOutputBufferRenderer() = default;
bool CodecOutputBufferRenderer::RenderToTextureOwnerBackBuffer( bool CodecOutputBufferRenderer::RenderToTextureOwnerBackBuffer() {
BlockingMode blocking_mode) {
DCHECK_NE(phase_, Phase::kInFrontBuffer); DCHECK_NE(phase_, Phase::kInFrontBuffer);
if (phase_ == Phase::kInBackBuffer) if (phase_ == Phase::kInBackBuffer)
return true; return true;
...@@ -65,12 +78,10 @@ bool CodecOutputBufferRenderer::RenderToTextureOwnerBackBuffer( ...@@ -65,12 +78,10 @@ bool CodecOutputBufferRenderer::RenderToTextureOwnerBackBuffer(
if (!codec_buffer_wait_coordinator_) if (!codec_buffer_wait_coordinator_)
return false; return false;
// Wait for a previous frame available so we don't confuse it with the one // Don't render frame if one is already pending.
// we're about to release. // RenderToTextureOwnerFrontBuffer will wait before calling this.
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) { if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
if (blocking_mode == BlockingMode::kForbidBlocking) return false;
return false;
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
} }
if (!output_buffer_->ReleaseToSurface()) { if (!output_buffer_->ReleaseToSurface()) {
phase_ = Phase::kInvalidated; phase_ = Phase::kInvalidated;
...@@ -98,32 +109,45 @@ bool CodecOutputBufferRenderer::RenderToTextureOwnerFrontBuffer( ...@@ -98,32 +109,45 @@ bool CodecOutputBufferRenderer::RenderToTextureOwnerFrontBuffer(
if (phase_ == Phase::kInvalidated) if (phase_ == Phase::kInvalidated)
return false; return false;
// Render it to the back buffer if it's not already there.
if (!RenderToTextureOwnerBackBuffer())
return false;
// The image is now in the back buffer, so promote it to the front buffer.
phase_ = Phase::kInFrontBuffer;
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable())
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current = std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
MakeCurrentIfNeeded( MakeCurrentIfNeeded(
codec_buffer_wait_coordinator_->texture_owner().get()); codec_buffer_wait_coordinator_->texture_owner().get());
// If updating the image will implicitly update the texture bindings then // If updating the image will implicitly update the texture bindings then
// restore if requested or the update needed a context switch. // restore if requested or the update needed a context switch.
bool should_restore_bindings = base::Optional<ScopedRestoreTextureBinding> scoped_restore_texture;
codec_buffer_wait_coordinator_->texture_owner() if (codec_buffer_wait_coordinator_->texture_owner()
->binds_texture_on_update() && ->binds_texture_on_update() &&
(bindings_mode == BindingsMode::kRestoreIfBound || !!scoped_make_current); (bindings_mode == BindingsMode::kRestoreIfBound ||
!!scoped_make_current)) {
scoped_restore_texture.emplace();
}
// Render it to the back buffer if it's not already there.
if (phase_ != Phase::kInBackBuffer) {
// Wait for a previous frame available so we don't confuse it with the one
// we're about to render.
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
// We must call update tex image if we did get OnFrameAvailable, otherwise
// we will stop receiving callbacks (see https://crbug.com/c/1113203)
codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
}
if (!RenderToTextureOwnerBackBuffer()) {
// RenderTotextureOwnerBackBuffer can fail now only if ReleaseToSurface
// failed.
DCHECK(phase_ == Phase::kInvalidated);
return false;
}
}
// The image is now in the back buffer, so promote it to the front buffer.
phase_ = Phase::kInFrontBuffer;
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable())
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
GLint bound_service_id = 0;
if (should_restore_bindings)
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage(); codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
EnsureBoundIfNeeded(bindings_mode); EnsureBoundIfNeeded(bindings_mode);
if (should_restore_bindings)
glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
return true; return true;
} }
......
...@@ -22,8 +22,6 @@ namespace media { ...@@ -22,8 +22,6 @@ namespace media {
class MEDIA_GPU_EXPORT CodecOutputBufferRenderer { class MEDIA_GPU_EXPORT CodecOutputBufferRenderer {
public: public:
using BindingsMode = gpu::StreamTextureSharedImageInterface::BindingsMode; using BindingsMode = gpu::StreamTextureSharedImageInterface::BindingsMode;
// Whether RenderToTextureOwnerBackBuffer may block or not.
enum class BlockingMode { kForbidBlocking, kAllowBlocking };
CodecOutputBufferRenderer( CodecOutputBufferRenderer(
std::unique_ptr<CodecOutputBuffer> output_buffer, std::unique_ptr<CodecOutputBuffer> output_buffer,
...@@ -52,11 +50,9 @@ class MEDIA_GPU_EXPORT CodecOutputBufferRenderer { ...@@ -52,11 +50,9 @@ class MEDIA_GPU_EXPORT CodecOutputBufferRenderer {
// Renders this image to the back buffer of its texture owner. Only valid if // Renders this image to the back buffer of its texture owner. Only valid if
// is_texture_owner_backed(). Returns true if the buffer is in the back // is_texture_owner_backed(). Returns true if the buffer is in the back
// buffer. Returns false if the buffer was invalidated. // buffer. Returns false if the buffer was invalidated.
// |blocking_mode| indicates whether this should (a) wait for any previously // RenderToTextureOwnerBackBuffer() will not block if there is any previously
// pending rendered frame before rendering this one, or (b) fail if a wait // pending frame and will return false in this case.
// is required. bool RenderToTextureOwnerBackBuffer();
bool RenderToTextureOwnerBackBuffer(
BlockingMode blocking_mode = BlockingMode::kAllowBlocking);
// Whether the codec buffer has been rendered to the front buffer. // Whether the codec buffer has been rendered to the front buffer.
bool was_rendered_to_front_buffer() const { bool was_rendered_to_front_buffer() const {
......
...@@ -90,8 +90,7 @@ void MEDIA_GPU_EXPORT MaybeRenderEarly(std::vector<Image*>* image_vector_ptr) { ...@@ -90,8 +90,7 @@ void MEDIA_GPU_EXPORT MaybeRenderEarly(std::vector<Image*>* image_vector_ptr) {
// Try to render to the back buffer, but don't wait for any previous frame. // Try to render to the back buffer, but don't wait for any previous frame.
// While this does make it more likely that we'll have to wait the next time // While this does make it more likely that we'll have to wait the next time
// we draw, it does prevent us from waiting on frames we don't plan to draw. // we draw, it does prevent us from waiting on frames we don't plan to draw.
images[back_buffer_index]->RenderToTextureOwnerBackBuffer( images[back_buffer_index]->RenderToTextureOwnerBackBuffer();
CodecImage::BlockingMode::kForbidBlocking);
} }
} }
......
...@@ -35,20 +35,17 @@ struct MockImage { ...@@ -35,20 +35,17 @@ struct MockImage {
} }
if (expectation == kRenderToBackBuffer) { if (expectation == kRenderToBackBuffer) {
EXPECT_CALL(*this, RenderToTextureOwnerBackBuffer( EXPECT_CALL(*this, RenderToTextureOwnerBackBuffer())
CodecImage::BlockingMode::kForbidBlocking))
.WillOnce(Return(phase != kInvalidated)); .WillOnce(Return(phase != kInvalidated));
} else { } else {
EXPECT_CALL(*this, RenderToTextureOwnerBackBuffer( EXPECT_CALL(*this, RenderToTextureOwnerBackBuffer()).Times(0);
CodecImage::BlockingMode::kForbidBlocking))
.Times(0);
} }
} }
MOCK_METHOD0(was_rendered_to_front_buffer, bool()); MOCK_METHOD0(was_rendered_to_front_buffer, bool());
MOCK_METHOD0(is_texture_owner_backed, bool()); MOCK_METHOD0(is_texture_owner_backed, bool());
MOCK_METHOD0(RenderToFrontBuffer, bool()); MOCK_METHOD0(RenderToFrontBuffer, bool());
MOCK_METHOD1(RenderToTextureOwnerBackBuffer, bool(CodecImage::BlockingMode)); MOCK_METHOD0(RenderToTextureOwnerBackBuffer, bool());
}; };
class MaybeRenderEarlyTest : public testing::Test { class MaybeRenderEarlyTest : public testing::Test {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment