Index: ppapi/examples/video_decoder/video_decoder_session.cc |
diff --git a/ppapi/examples/video_decoder/video_decoder_session.cc b/ppapi/examples/video_decoder/video_decoder_session.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..98bac3e49a9a44dcbd6a4f91b128156edd69d5f1 |
--- /dev/null |
+++ b/ppapi/examples/video_decoder/video_decoder_session.cc |
@@ -0,0 +1,595 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ppapi/examples/video_decoder/video_decoder_session.h" |
+ |
+#include <cstring> |
+#include <fstream> |
+#include <iostream> |
+ |
+#include "ppapi/c/dev/pp_graphics_3d_dev.h" |
+#include "ppapi/c/dev/ppb_buffer_dev.h" |
+#include "ppapi/c/pp_errors.h" |
+#include "ppapi/cpp/dev/context_3d_dev.h" |
+#include "ppapi/cpp/dev/surface_3d_dev.h" |
+#include "ppapi/cpp/dev/video_decoder_dev.h" |
+#include "ppapi/lib/gl/include/GLES2/gl2.h" |
+ |
+// Pull-based video source to read video data from a file. |
+class TestVideoSource { |
+ public: |
+ TestVideoSource() |
+ : file_length_(0), |
+ offset_(0) {} |
+ |
+ ~TestVideoSource() {} |
+ |
+ bool Open(const std::string& url) { |
+ // TODO(vmr): Use file_util::ReadFileToString or equivalent to read the file |
+ // if one-shot reading is used. |
+ std::ifstream* file = |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
scoped_ptr, then delete the deletes below :)
Ville-Mikko Rautio
2011/06/03 13:24:39
I am in impression that relying on anything from C
|
+ new std::ifstream(url.c_str(), |
+ std::ios::in | std::ios::binary | std::ios::ate); |
+ if (!file->good()) { |
+ delete file; |
+ return false; |
+ } |
+ file->seekg(0, std::ios::end); |
+ uint32_t length = file->tellg(); |
+ file->seekg(0, std::ios::beg); |
+ mem_ = new uint8_t[length]; |
+ file->read(reinterpret_cast<char*>(mem_), length); |
+ file_length_ = length; |
+ file->close(); |
+ delete file; |
+ return true; |
+ } |
+ |
+ // Reads next packet from the input stream. |
+ // Returns number of read bytes on success, 0 on when there was no valid data |
+ // to be read and -1 if user gave NULL or too small buffer. |
+ // TODO(vmr): Modify to differentiate between errors and EOF. |
+ int32_t Read(uint8_t* target_mem, uint32_t size) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
It looks like you are doing some decoding to split
Ville-Mikko Rautio
2011/06/03 13:24:39
There is possibility to pass the whole bitstream f
|
+ if (!target_mem) |
+ return -1; |
+ uint8_t* unit_begin = NULL; |
+ uint8_t* unit_end = NULL; |
+ uint8_t* ptr = mem_ + offset_; |
+ while (offset_ + 4 < file_length_) { |
+ if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 1) { |
+ // start code found |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Complete sentence + explanation of start code.
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ if (!unit_begin) { |
+ unit_begin = ptr; |
+ } else { |
+ // back-up 1 byte. |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Complete sentence.
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ unit_end = ptr; |
+ break; |
+ } |
+ } |
+ ptr++; |
+ offset_++; |
+ } |
+ if (unit_begin && offset_ + 4 == file_length_) { |
+ // Last unit. Set the unit_end to point to the last byte. |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
The last buffer begins _and_ ends with 0001?
In ot
Ville-Mikko Rautio
2011/06/03 13:24:39
There's no 0001 at the end. +4 needed because of t
|
+ unit_end = ptr + 4; |
+ offset_ += 4; |
+ } else if (!unit_begin || !unit_end) { |
+ // No unit start codes found in buffer. |
+ return 0; |
+ } |
+ if (static_cast<int32_t>(size) >= unit_end - unit_begin) { |
+ memcpy(target_mem, unit_begin, unit_end - unit_begin); |
+ return unit_end - unit_begin; |
+ } |
+ // Rewind to the beginning start code if there is one as it should be |
+ // returned with next Read(). |
+ offset_ = unit_begin - mem_; |
+ return -1; |
+ } |
+ |
+ private: |
+ uint32_t file_length_; |
+ uint32_t offset_; |
+ uint8_t* mem_; |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
scoped_array? At the very least you need to delete
Ville-Mikko Rautio
2011/06/03 13:24:39
True. Added delete for destructor since I am not s
|
+}; |
+ |
+LocalVideoBitstreamSource::LocalVideoBitstreamSource(std::string filename) |
+ : file_(filename), |
+ video_source_(new TestVideoSource()), |
+ video_source_open_(false) { |
+} |
+ |
+LocalVideoBitstreamSource::~LocalVideoBitstreamSource() { |
+ delete video_source_; |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
No longer needed w/ scoped_ptr
Ville-Mikko Rautio
2011/06/03 13:24:39
Ditto.
|
+} |
+ |
+bool LocalVideoBitstreamSource::GetBitstreamUnit( |
+ void* target_mem, |
+ uint32_t target_mem_size_in_bytes, |
+ int32_t* unit_size_in_bytes) { |
+ if (!video_source_open_) { |
+ if (!video_source_->Open(file_)) { |
+ return false; |
+ } |
+ video_source_open_ = true; |
+ } |
+ int32_t read_bytes = video_source_->Read(static_cast<uint8_t*>(target_mem), |
+ target_mem_size_in_bytes); |
+ if (read_bytes <= 0) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: no {} for one-line condition
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ *unit_size_in_bytes = read_bytes; |
+ return true; |
+} |
+ |
+VideoDecoderSessionClient::~VideoDecoderSessionClient() { |
+} |
+ |
+// Constants used by VideoDecoderSession. |
+static const int32_t kBitstreamBufferCount = 3; |
+static const int32_t kBitstreamBufferSize = 256 * 1024 * 1024; |
+static const int32_t kDefaultWidth = 640; |
+static const int32_t kDefaultHeight = 480; |
+ |
+bool VideoDecoderSession::Initialize( |
+ const std::vector<uint32_t>& decoder_config, |
+ pp::CompletionCallback completion_callback) { |
+ assert(video_source_ && display_); |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Do this check in the constructor.
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ // Default implementation just assumes everything is set up. |
+ if (!AllocateInputBuffers()) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: No {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ pp::CompletionCallback cb = cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnInitializeDone, completion_callback); |
+ video_decoder_ = new pp::VideoDecoder(instance_, decoder_config, cb, this); |
+ if (!video_decoder_) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: No {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ return true; |
+} |
+ |
+bool VideoDecoderSession::Run(pp::CompletionCallback completion_callback) { |
+ assert(state_ == kInitialized); |
+ // Start the streaming by dispatching the first buffers one by one. |
+ for (std::map<int32_t, PP_VideoBitstreamBuffer_Dev>::iterator it = |
+ bitstream_buffers_.begin(); |
+ it == bitstream_buffers_.end(); |
+ it++) { |
+ if (!ReadAndDispatchBitstreamUnit((*it).first)) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ } |
+ // Once streaming has been started, we're running. |
+ ChangeState(kRunning); |
+ completion_callback.Run(PP_OK); |
+ return true; |
+} |
+ |
+bool VideoDecoderSession::Stop(pp::CompletionCallback completion_callback) { |
+ assert(state_ == kRunning); |
+ // Stop the playback. |
+ ChangeState(kInitialized); |
+ return true; |
+} |
+ |
+bool VideoDecoderSession::Flush(pp::CompletionCallback completion_callback) { |
+ assert(state_ == kRunning); |
+ // Issue the flush request. |
+ ChangeState(kFlushing); |
+ video_decoder_->Flush(cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnUserFlushDone, state_, completion_callback)); |
+ return true; |
+} |
+ |
+bool VideoDecoderSession::Teardown(pp::CompletionCallback completion_callback) { |
+ assert(state_ == kInitialized); |
+ // Teardown the resources. |
+ FreeInputBuffers(); |
+ ChangeState(kCreated); |
+ completion_callback.Run(PP_OK); |
+ return true; |
+} |
+ |
+void VideoDecoderSession::ProvidePictureBuffers( |
+ uint32_t requested_num_of_buffers, |
+ const std::vector<uint32_t>& buffer_properties) { |
vjain
2011/05/31 22:29:07
What is the purpose of buffer_properties?
Will GPU
Ville-Mikko Rautio
2011/06/01 11:50:08
Buffer properties will contain width & height and
vrk (LEFT CHROMIUM)
2011/06/01 22:41:31
Actually, this C++ API is not up to date with the
Ville-Mikko Rautio
2011/06/03 13:24:39
C++ API change (http://codereview.chromium.org/708
|
+ // Currently we support only GLES buffer allocation. |
+ std::vector<PP_GLESBuffer_Dev> buffers; |
+ for (uint32_t i = 0; i < requested_num_of_buffers; i++) { |
+ PP_GLESBuffer_Dev gles_buffer; |
+ if (!display_->ProvideGLESPictureBuffer(buffer_properties, &gles_buffer)) { |
+ video_decoder_->Abort(cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnAbortDone)); |
+ return; |
+ } |
+ buffers.push_back(gles_buffer); |
+ } |
+ video_decoder_->AssignGLESBuffers(buffers.size(), buffers); |
+} |
+ |
+void VideoDecoderSession::DismissPictureBuffer(int32_t picture_buffer_id) { |
+ if (!display_->DismissPictureBuffer(picture_buffer_id)) { |
+ assert(!"Failed to dismiss picture buffer properly"); |
+ return; |
+ } |
+} |
+ |
+void VideoDecoderSession::PictureReady(const PP_Picture_Dev& picture) { |
+ display_->DrawPicture(picture, cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnDrawPictureDone, picture.picture_buffer_id)); |
+} |
+ |
+void VideoDecoderSession::NotifyEndOfStream() { |
+ end_of_stream_ = true; |
+ video_decoder_->Flush(cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnInternalFlushDone)); |
+} |
+ |
+void VideoDecoderSession::NotifyError(PP_VideoDecodeError_Dev error) { |
+ video_decoder_->Flush(cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnInternalFlushDone)); |
+} |
+ |
+int32_t VideoDecoderSession::GetUniqueId() { |
+ // Not exactly unique in the current form but close enough for use case. |
+ return next_id_++; |
+} |
+ |
+void VideoDecoderSession::OnInitializeDone(int32_t result, |
+ pp::CompletionCallback callback) { |
+ if (state_ != kCreated) { |
+ ChangeState(kCreated); |
+ callback.Run(PP_ERROR_ABORTED); |
+ } |
+ if (result != PP_OK) { |
+ ChangeState(kInitialized); |
+ callback.Run(result); |
+ } |
+ callback.Run(PP_OK); |
+} |
+ |
+void VideoDecoderSession::OnBitstreamBufferProcessed( |
vjain
2011/06/02 01:04:52
Hi Ville-Mikko,
When the GPU decoder returns the b
Ville-Mikko Rautio
2011/06/03 13:24:39
I am not sure what are you referring to. Bitstream
|
+ int32_t result, |
+ int32_t bitstream_buffer_id) { |
+ // Reuse each bitstream buffer that has been processed by reading data into it |
+ // as long as there is more and pass that for decoding. |
+ ReadAndDispatchBitstreamUnit(bitstream_buffer_id); |
+} |
+ |
+void VideoDecoderSession::OnDrawPictureDone(int32_t result, |
+ int32_t picture_buffer_id) { |
+ video_decoder_->ReusePictureBuffer(picture_buffer_id); |
+} |
+ |
+void VideoDecoderSession::OnUserFlushDone(int32_t result, |
+ State target_state, |
+ pp::CompletionCallback callback) { |
+ assert(state_ == kFlushing); |
+ // It was a Flush request, return to the state where we started. |
+ ChangeState(target_state); |
+ callback.Run(result); |
+} |
+ |
+void VideoDecoderSession::OnInternalFlushDone(int32_t result) { |
+ if (end_of_stream_) { |
+ // It was end of stream flush. |
+ video_decoder_->Abort(cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnAbortDone)); |
+ } else { |
+ assert(!"Unhandled flush completion!"); |
+ } |
+} |
+ |
+void VideoDecoderSession::OnAbortDone(int32_t result) { |
+ client_->OnSessionCompleted(result); |
+} |
+ |
+bool VideoDecoderSession::AllocateInputBuffers() { |
+ buffer_if_ = static_cast<const struct PPB_Buffer_Dev*>( |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
?? Why is this field being set here? Shouldn't thi
Ville-Mikko Rautio
2011/06/03 13:24:39
Moved to ctor and added assert for variable.
|
+ pp::Module::Get()->GetBrowserInterface(PPB_BUFFER_DEV_INTERFACE)); |
+ if (!buffer_if_) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: no {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Removed
|
+ return false; |
+ } |
+ // Allocate |kBitstreamBufferCount| bitstream buffers of |
+ // |kBitstreamBufferSize| bytes. |
+ for (int32_t i = 0; i < kBitstreamBufferCount; i++) { |
+ PP_VideoBitstreamBuffer_Dev bitstream_buffer; |
+ bitstream_buffer.data = buffer_if_->Create(instance_->pp_instance(), |
+ kBitstreamBufferSize); |
+ if (bitstream_buffer.data == 0) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: no {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ bitstream_buffer.size = 0; |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
This should be kBitstreamBufferSize with change me
Ville-Mikko Rautio
2011/06/03 13:24:39
This is the amount of valid data in buffer, so I d
|
+ bitstream_buffer.id = GetUniqueId(); |
+ bitstream_buffers_[bitstream_buffer.id] = bitstream_buffer; |
+ } |
+ return true; |
+} |
+ |
+void VideoDecoderSession::FreeInputBuffers() { |
+ std::map<int32_t, PP_VideoBitstreamBuffer_Dev>::iterator it; |
+ for (it = bitstream_buffers_.begin(); it != bitstream_buffers_.end(); it++) { |
+ std::pair<int32_t, PP_VideoBitstreamBuffer_Dev> pair = *it; |
+ PP_VideoBitstreamBuffer_Dev bitstream_buffer = pair.second; |
+ pp::Module::Get()->core()->ReleaseResource(bitstream_buffer.data); |
+ bitstream_buffers_.erase(it); |
+ } |
+} |
+ |
+bool VideoDecoderSession::ReadAndDispatchBitstreamUnit( |
+ int32_t bitstream_buffer_id) { |
+ // Get the target memory and read the bitstream unit into it. |
+ if (bitstream_buffers_.find(bitstream_buffer_id) == |
+ bitstream_buffers_.end()) { |
+ return false; |
+ } |
+ PP_VideoBitstreamBuffer_Dev bitstream_buffer = |
+ bitstream_buffers_[bitstream_buffer_id]; |
+ void* target_mem = buffer_if_->Map(bitstream_buffer.data); |
+ if (target_mem == NULL) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: no {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ uint32_t size_in_bytes = 0; |
+ if (!buffer_if_->Describe(bitstream_buffer.data, &size_in_bytes)) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: no {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ bool success = video_source_->GetBitstreamUnit(target_mem, size_in_bytes, |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
It's wasteful to allocate a buffer of kBitstreamBu
Ville-Mikko Rautio
2011/06/03 13:24:39
Already commented above. You cannot arbitrarily sp
|
+ &bitstream_buffer.size); |
+ if (!success) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Nit: no {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ // Dispatch the bitstream unit to the decoder. |
+ success = video_decoder_->Decode( |
+ bitstream_buffers_[bitstream_buffer_id], |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
bitstream_buffer?
Ville-Mikko Rautio
2011/06/03 13:24:39
Yep. Also, I changed the bitstream_buffer into ref
|
+ cb_factory_.NewCallback( |
+ &VideoDecoderSession::OnBitstreamBufferProcessed, |
+ bitstream_buffer_id)); |
+ // Finally unmap the buffer for this round. |
+ buffer_if_->Unmap(bitstream_buffer.data); |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Unmap deletes the bitstream data. Shouldn't we onl
Ville-Mikko Rautio
2011/06/03 13:24:39
True. Done.
|
+ return success; |
+} |
+ |
+void VideoDecoderSession::ChangeState(State to_state) { |
+ state_ = to_state; |
+} |
+ |
+int32_t VideoDecoderSession::next_id_ = 1; |
+ |
+// Pass-through vertex shader. |
+static const char kVertexShader[] = |
+ "precision highp float; precision highp int;\n" |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
I don't believe these precision specifiers are nec
Ville-Mikko Rautio
2011/06/03 13:24:39
I am by no means shader expert, so I'll just take
|
+ "varying vec2 interp_tc;\n" |
+ "\n" |
+ "attribute vec4 in_pos;\n" |
+ "attribute vec2 in_tc;\n" |
+ "\n" |
+ "void main() {\n" |
+ " interp_tc = in_tc;\n" |
+ " gl_Position = in_pos;\n" |
+ "}\n"; |
+ |
+// Color shader for EGLImage. |
+static const char kFragmentShaderEgl[] = |
+ "precision mediump float;\n" |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Ditto above.
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ "precision mediump int;\n" |
+ "varying vec2 interp_tc;\n" |
+ "\n" |
+ "uniform sampler2D tex;\n" |
+ "\n" |
+ "void main() {\n" |
+ " gl_FragColor = texture2D(tex, interp_tc);\n" |
+ "}\n"; |
+ |
+// Buffer size for compile errors. |
+static const unsigned int kShaderErrorSize = 4096; |
+ |
+void GLES2Display::Graphics3DContextLost() { |
+ assert(!"GLES2: Unexpectedly lost graphics context"); |
+} |
+ |
+bool GLES2Display::Initialize() { |
+ if (!InitGL(surface_size_.width, surface_size_.height)) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
nit: no {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ ProgramShaders(); |
+ return true; |
+} |
+ |
+bool GLES2Display::ProvideGLESPictureBuffer( |
+ const std::vector<uint32_t>& buffer_properties, |
+ PP_GLESBuffer_Dev* picture_buffer) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Would make sense to also do the initial call to Te
Ville-Mikko Rautio
2011/06/03 13:24:39
I don't know GL well, so I will need your help in
|
+ GLuint texture; |
+ // Generate texture and bind (effectively allocate) it. |
+ gles2_if_->GenTextures(context_->pp_resource(), 1, &texture); |
+ gles2_if_->BindTexture(context_->pp_resource(), GL_TEXTURE_2D, texture); |
+ picture_buffer->context = 0; // TODO(vmr): Get proper context id. |
+ picture_buffer->texture_id = texture; |
+ picture_buffer->info.id = VideoDecoderSession::GetUniqueId(); |
+ picture_buffer->info.size.width = surface_size_.width; |
+ picture_buffer->info.size.height = surface_size_.height; |
+ // Store the values into the map for GLES buffers. |
+ gles_buffers_[picture_buffer->info.id] = *picture_buffer; |
+ assertNoGLError(); |
+ return true; |
+} |
+ |
+bool GLES2Display::DismissPictureBuffer(int32_t picture_buffer_id) { |
+ gles2_if_->DeleteTextures(context_->pp_resource(), 1, |
+ &gles_buffers_[picture_buffer_id].texture_id); |
+ gles_buffers_.erase(picture_buffer_id); |
+ return true; |
+} |
+ |
+bool GLES2Display::DrawPicture(const PP_Picture_Dev& picture, |
+ pp::CompletionCallback completion_callback) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
nit: add space before pp
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ // Decoder has finished decoding picture into the texture, we'll have to just |
+ // draw the texture to the color buffer and swap the surfaces. |
+ // Clear the color buffer. |
+ gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT | |
+ GL_DEPTH_BUFFER_BIT); |
+ // Load the texture into texture unit 0. |
+ gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0); |
+ gles2_if_->BindTexture(context_->pp_resource(), GL_TEXTURE_2D, |
+ gles_buffers_[picture.picture_buffer_id].texture_id); |
+ // Draw the texture. |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
You never copy data into the texture (TexImage2D/M
Ville-Mikko Rautio
2011/06/03 13:24:39
See above comment about ProvideGLESPictureBuffer.
|
+ gles2_if_->DrawArrays(context_->pp_resource(), GL_TRIANGLE_STRIP, 0, 4); |
+ // Force the execution of pending commands. |
+ // TODO(vmr): Do we have to do this? Can we rely command buffer to execute the |
+ // commands without Finish call? |
+ gles2_if_->Finish(context_->pp_resource()); |
+ assertNoGLError(); |
+ |
+ int32_t error = surface_->SwapBuffers(completion_callback); |
+ if (error != PP_OK) { |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
nit: no {}
Ville-Mikko Rautio
2011/06/03 13:24:39
Done.
|
+ return false; |
+ } |
+ assertNoGLError(); |
+ return true; |
+} |
+ |
+void GLES2Display::assertNoGLError() { |
+ assert(!gles2_if_->GetError(context_->pp_resource())); |
+} |
+ |
+bool GLES2Display::InitGL(int width, int height) { |
+ assert(width && height); |
+ gles2_if_ = static_cast<const struct PPB_OpenGLES2_Dev*>( |
+ pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_DEV_INTERFACE)); |
+ // Firstly, we need OpenGL ES context associated with the display our plugin |
+ // is rendering to. |
+ if (context_) delete(context_); |
+ context_ = new pp::Context3D_Dev(*instance_, 0, pp::Context3D_Dev(), NULL); |
+ assert(!context_->is_null()); |
+ // Then we need surface bound to our fresh context. We'll be actually drawing |
+ // on this surface and swapping that surface to refresh the displayable data |
+ // of the plugin. |
+ int32_t surface_attributes[] = { |
+ PP_GRAPHICS3DATTRIB_WIDTH, surface_size_.width, |
+ PP_GRAPHICS3DATTRIB_HEIGHT, surface_size_.height, |
+ PP_GRAPHICS3DATTRIB_NONE |
+ }; |
+ if (surface_) delete(surface_); |
+ surface_ = new pp::Surface3D_Dev(*instance_, 0, surface_attributes); |
+ assert(!surface_->is_null()); |
+ int32_t bind_error = context_->BindSurfaces(*surface_, *surface_); |
+ if (!bind_error) { |
+ assert(bind_error); |
+ } |
+ assertNoGLError(); |
+ |
+ bool success = instance_->BindGraphics(*surface_); |
+ if (!success) { |
+ assert(success); |
+ } |
+ // Clear the color buffer with opaque white for starters. |
+ gles2_if_->ClearColor(context_->pp_resource(), 1.0, 1.0, 1.0, 0.0); |
+ gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT); |
+ // Set the viewport to match the whole GL window. |
+ gles2_if_->Viewport(context_->pp_resource(), 0, 0, surface_size_.width, |
+ surface_size_.height); |
+ assertNoGLError(); |
+ return true; |
+} |
+ |
+void GLES2Display::CreateShader(GLuint program, GLenum type, |
+ const char* source, |
+ int size) { |
+ GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type); |
+ gles2_if_->ShaderSource( |
+ context_->pp_resource(), shader, 1, &source, &size); |
+ gles2_if_->CompileShader(context_->pp_resource(), shader); |
+ |
+ int result = GL_FALSE; |
+ gles2_if_->GetShaderiv( |
+ context_->pp_resource(), shader, GL_COMPILE_STATUS, &result); |
+ if (!result) { |
+ char log[kShaderErrorSize]; |
+ int len = 0; |
+ gles2_if_->GetShaderInfoLog(context_->pp_resource(), shader, |
+ kShaderErrorSize - 1, &len, log); |
+ log[len] = 0; |
+ assert(result); |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
You never use log... change to fail with "log" as
Ville-Mikko Rautio
2011/06/03 13:24:39
In Pepper code?
|
+ } |
+ gles2_if_->AttachShader(context_->pp_resource(), program, shader); |
+ gles2_if_->DeleteShader(context_->pp_resource(), shader); |
+} |
+ |
+void GLES2Display::LinkProgram(const PPB_OpenGLES2_Dev* gles2_if_ ) { |
+ gles2_if_->LinkProgram(context_->pp_resource(), program_); |
+ int result = GL_FALSE; |
+ gles2_if_->GetProgramiv(context_->pp_resource(), program_, GL_LINK_STATUS, |
+ &result); |
+ if (!result) { |
+ char log[kShaderErrorSize]; |
+ int len = 0; |
+ gles2_if_->GetProgramInfoLog(context_->pp_resource(), program_, |
+ kShaderErrorSize - 1, &len, log); |
+ log[len] = 0; |
+ assert(result); |
+ } |
+ gles2_if_->UseProgram(context_->pp_resource(), program_); |
+} |
+ |
+void GLES2Display::ProgramShaders() { |
+ // Vertices for a full screen quad. |
+ static const float kVertices[] = { |
+ -1.f, 1.f, |
+ -1.f, -1.f, |
+ 1.f, 1.f, |
+ 1.f, -1.f, |
+ }; |
+ |
+ // Texture Coordinates mapping the entire texture for EGL image. |
+ static const float kTextureCoordsEgl[] = { |
+ 0, 1, |
+ 0, 0, |
+ 1, 1, |
+ 1, 0, |
+ }; |
+ program_ = gles2_if_->CreateProgram(context_->pp_resource()); |
+ |
+ // Create shader for EGL image |
+ CreateShader(program_, GL_VERTEX_SHADER, |
+ kVertexShader, sizeof(kVertexShader)); |
+ CreateShader(program_, GL_FRAGMENT_SHADER, |
+ kFragmentShaderEgl, sizeof(kFragmentShaderEgl)); |
+ LinkProgram(gles2_if_); |
+ |
+ assertNoGLError(); |
+ // Bind parameters. |
+ gles2_if_->Uniform1i(context_->pp_resource(), gles2_if_-> |
+ GetUniformLocation(context_->pp_resource(), program_, |
+ "tex"), 0); |
+ gles2_if_->GenBuffers(context_->pp_resource(), 1, &vertex_buffer_); |
+ gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, |
+ vertex_buffer_); |
+ gles2_if_->BufferData(context_->pp_resource(), GL_ARRAY_BUFFER, |
+ 8 * sizeof(kVertices[0]), kVertices, GL_STREAM_DRAW); |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Should this be GL_STATIC_DRAW?
Ville-Mikko Rautio
2011/06/03 13:24:39
I think both will work. I took your suggestion. Do
|
+ |
+ assertNoGLError(); |
+ int pos_location = gles2_if_->GetAttribLocation(context_->pp_resource(), |
+ program_, "in_pos"); |
+ gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location); |
+ gles2_if_->VertexAttribPointer(context_->pp_resource(), pos_location, 2, |
+ GL_FLOAT, GL_FALSE, 0, 0); |
+ |
+ assertNoGLError(); |
+ gles2_if_->GenBuffers(context_->pp_resource(), 1, &fragment_buffer_); |
+ gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, |
+ fragment_buffer_); |
+ gles2_if_->BufferData(context_->pp_resource(), GL_ARRAY_BUFFER, |
+ 8 * sizeof(kTextureCoordsEgl[0]), |
+ kTextureCoordsEgl, GL_STREAM_DRAW); |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
Ditto above.
Ville-Mikko Rautio
2011/06/03 13:24:39
Ditto.
|
+ assertNoGLError(); |
+ int tc_location = gles2_if_->GetAttribLocation(context_->pp_resource(), |
+ program_, "in_tc"); |
+ gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location); |
+ gles2_if_->VertexAttribPointer(context_->pp_resource(), tc_location, 2, |
+ GL_FLOAT, GL_FALSE, 0, kTextureCoordsEgl); |
+ gles2_if_->VertexAttribPointer(context_->pp_resource(), tc_location, 2, |
+ GL_FLOAT, GL_FALSE, 0, 0); |
+ gles2_if_->Enable(context_->pp_resource(), GL_DEPTH_TEST); |
vrk (LEFT CHROMIUM)
2011/06/02 01:47:02
What is GL_DEPTH_TEST?
Ville-Mikko Rautio
2011/06/03 13:24:39
I believe it is feature of the fragment pipeline i
|
+ assertNoGLError(); |
+} |
+ |