diff options
author | apatrick@google.com <apatrick@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-11 19:47:26 +0000 |
---|---|---|
committer | apatrick@google.com <apatrick@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-11 19:47:26 +0000 |
commit | 0d85ea6e9786806964ec2a05a12dd2e0a05bdb0c (patch) | |
tree | 552a503dd6c198dcaaf88d7cf9fcecf545fd8b69 /o3d/gpu | |
parent | 7c9109b43243c43c427c337c7ac3c049fe77a5b9 (diff) | |
download | chromium_src-0d85ea6e9786806964ec2a05a12dd2e0a05bdb0c.zip chromium_src-0d85ea6e9786806964ec2a05a12dd2e0a05bdb0c.tar.gz chromium_src-0d85ea6e9786806964ec2a05a12dd2e0a05bdb0c.tar.bz2 |
Recomitting 31676 with fix.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31692 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/gpu')
126 files changed, 27582 insertions, 0 deletions
diff --git a/o3d/gpu/command_buffer/client/cmd_buffer_helper.cc b/o3d/gpu/command_buffer/client/cmd_buffer_helper.cc new file mode 100644 index 0000000..e6a49d1 --- /dev/null +++ b/o3d/gpu/command_buffer/client/cmd_buffer_helper.cc @@ -0,0 +1,197 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the command buffer helper class. + +#include "gpu/command_buffer/client/cmd_buffer_helper.h" +#include "gpu/np_utils/np_utils.h" + +namespace command_buffer { + +using gpu_plugin::CommandBuffer; +using gpu_plugin::NPBrowser; +using gpu_plugin::NPInvoke; +using gpu_plugin::NPObjectPointer; + +CommandBufferHelper::CommandBufferHelper( + NPP npp, + const NPObjectPointer<CommandBuffer>& command_buffer) + : npp_(npp), + command_buffer_(command_buffer), + entries_(NULL), + entry_count_(0), + token_(0), + last_token_read_(-1), + get_(0), + put_(0) { +} + +bool CommandBufferHelper::Initialize() { + ring_buffer_ = command_buffer_->GetRingBuffer(); + if (!ring_buffer_) + return false; + + // Map the ring buffer into this process. + if (!ring_buffer_->Map(ring_buffer_->max_size())) + return false; + + entries_ = static_cast<CommandBufferEntry*>(ring_buffer_->memory()); + entry_count_ = command_buffer_->GetSize(); + get_ = command_buffer_->GetGetOffset(); + put_ = command_buffer_->GetPutOffset(); + last_token_read_ = command_buffer_->GetToken(); + + return true; +} + +CommandBufferHelper::~CommandBufferHelper() { +} + +bool CommandBufferHelper::Flush() { + get_ = command_buffer_->SyncOffsets(put_); + return !command_buffer_->GetErrorStatus(); +} + +// Calls Flush() and then waits until the buffer is empty. Break early if the +// error is set. +bool CommandBufferHelper::Finish() { + do { + // Do not loop forever if the flush fails, meaning the command buffer reader + // has shutdown). + if (!Flush()) + return false; + } while (put_ != get_); + + return true; +} + +// Inserts a new token into the command stream. It uses an increasing value +// scheme so that we don't lose tokens (a token has passed if the current token +// value is higher than that token). Calls Finish() if the token value wraps, +// which will be rare. +int32 CommandBufferHelper::InsertToken() { + // Increment token as 31-bit integer. Negative values are used to signal an + // error. + token_ = (token_ + 1) & 0x7FFFFFFF; + CommandBufferEntry args; + args.value_uint32 = token_; + const uint32 kSetToken = 1; // TODO(gman): add a common set of commands. + AddCommand(kSetToken, 1, &args); + if (token_ == 0) { + // we wrapped + Finish(); + last_token_read_ = command_buffer_->GetToken(); + DCHECK_EQ(token_, last_token_read_); + } + return token_; +} + +// Waits until the current token value is greater or equal to the value passed +// in argument. +void CommandBufferHelper::WaitForToken(int32 token) { + // Return immediately if corresponding InsertToken failed. + if (token < 0) + return; + if (last_token_read_ >= token) return; // fast path. + if (token > token_) return; // we wrapped + Flush(); + last_token_read_ = command_buffer_->GetToken(); + while (last_token_read_ < token) { + if (get_ == put_) { + LOG(FATAL) << "Empty command buffer while waiting on a token."; + return; + } + // Do not loop forever if the flush fails, meaning the command buffer reader + // has shutdown. + if (!Flush()) + return; + last_token_read_ = command_buffer_->GetToken(); + } +} + +// Waits for available entries, basically waiting until get >= put + count + 1. +// It actually waits for contiguous entries, so it may need to wrap the buffer +// around, adding noops. Thus this function may change the value of put_. +// The function will return early if an error occurs, in which case the +// available space may not be available. +void CommandBufferHelper::WaitForAvailableEntries(int32 count) { + CHECK(count < entry_count_); + if (put_ + count > entry_count_) { + // There's not enough room between the current put and the end of the + // buffer, so we need to wrap. We will add noops all the way to the end, + // but we need to make sure get wraps first, actually that get is 1 or + // more (since put will wrap to 0 after we add the noops). + DCHECK_LE(1, put_); + Flush(); + while (get_ > put_ || get_ == 0) { + // Do not loop forever if the flush fails, meaning the command buffer + // reader has shutdown. + if (!Flush()) + return; + } + // Add the noops. By convention, a noop is a command 0 with no args. + // TODO(apatrick): A noop can have a size. It would be better to add a + // single noop with a variable size. Watch out for size limit on + // individual commands. + CommandHeader header; + header.size = 1; + header.command = 0; + while (put_ < entry_count_) { + entries_[put_++].value_header = header; + } + put_ = 0; + } + // If we have enough room, return immediatly. + if (count <= AvailableEntries()) return; + // Otherwise flush, and wait until we do have enough room. + Flush(); + while (AvailableEntries() < count) { + // Do not loop forever if the flush fails, meaning the command buffer reader + // has shutdown. + if (!Flush()) + return; + } +} + +CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) { + WaitForAvailableEntries(entries); + CommandBufferEntry* space = &entries_[put_]; + put_ += entries; + return space; +} + +parse_error::ParseError CommandBufferHelper::GetParseError() { + int32 parse_error = command_buffer_->ResetParseError(); + return static_cast<parse_error::ParseError>(parse_error); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/client/cmd_buffer_helper.h b/o3d/gpu/command_buffer/client/cmd_buffer_helper.h new file mode 100644 index 0000000..d63a53e --- /dev/null +++ b/o3d/gpu/command_buffer/client/cmd_buffer_helper.h @@ -0,0 +1,212 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the command buffer helper class. + +#ifndef GPU_COMMAND_BUFFER_CLIENT_CROSS_CMD_BUFFER_HELPER_H_ +#define GPU_COMMAND_BUFFER_CLIENT_CROSS_CMD_BUFFER_HELPER_H_ + +#include "gpu/command_buffer/common/logging.h" +#include "gpu/command_buffer/common/constants.h" +#include "gpu/command_buffer/common/cmd_buffer_common.h" +#include "gpu/gpu_plugin/command_buffer.h" +#include "gpu/np_utils/np_object_pointer.h" + +namespace command_buffer { + +// Command buffer helper class. This class simplifies ring buffer management: +// it will allocate the buffer, give it to the buffer interface, and let the +// user add commands to it, while taking care of the synchronization (put and +// get). It also provides a way to ensure commands have been executed, through +// the token mechanism: +// +// helper.AddCommand(...); +// helper.AddCommand(...); +// int32 token = helper.InsertToken(); +// helper.AddCommand(...); +// helper.AddCommand(...); +// [...] +// +// helper.WaitForToken(token); // this doesn't return until the first two +// // commands have been executed. +class CommandBufferHelper { + public: + CommandBufferHelper( + NPP npp, + const gpu_plugin::NPObjectPointer<gpu_plugin::CommandBuffer>& + command_buffer); + virtual ~CommandBufferHelper(); + + bool Initialize(); + + // Flushes the commands, setting the put pointer to let the buffer interface + // know that new commands have been added. After a flush returns, the command + // buffer service is aware of all pending commands and it is guaranteed to + // have made some progress in processing them. Returns whether the flush was + // successful. The flush will fail if the command buffer service has + // disconnected. + bool Flush(); + + // Waits until all the commands have been executed. Returns whether it + // was successful. The function will fail if the command buffer service has + // disconnected. + bool Finish(); + + // Waits until a given number of available entries are available. + // Parameters: + // count: number of entries needed. This value must be at most + // the size of the buffer minus one. + void WaitForAvailableEntries(int32 count); + + // Adds a command data to the command buffer. This may wait until sufficient + // space is available. + // Parameters: + // entries: The command entries to add. + // count: The number of entries. + void AddCommandData(const CommandBufferEntry* entries, int32 count) { + WaitForAvailableEntries(count); + for (; count > 0; --count) { + entries_[put_++] = *entries++; + } + DCHECK_LE(put_, entry_count_); + if (put_ == entry_count_) put_ = 0; + } + + // A typed version of AddCommandData. + template <typename T> + void AddTypedCmdData(const T& cmd) { + AddCommandData(reinterpret_cast<const CommandBufferEntry*>(&cmd), + ComputeNumEntries(sizeof(cmd))); + } + + // Adds a command to the command buffer. This may wait until sufficient space + // is available. + // Parameters: + // command: the command index. + // arg_count: the number of arguments for the command. + // args: the arguments for the command (these are copied before the + // function returns). + void AddCommand(int32 command, + int32 arg_count, + const CommandBufferEntry *args) { + CommandHeader header; + header.size = arg_count + 1; + header.command = command; + WaitForAvailableEntries(header.size); + entries_[put_++].value_header = header; + for (int i = 0; i < arg_count; ++i) { + entries_[put_++] = args[i]; + } + DCHECK_LE(put_, entry_count_); + if (put_ == entry_count_) put_ = 0; + } + + // Inserts a new token into the command buffer. This token either has a value + // different from previously inserted tokens, or ensures that previously + // inserted tokens with that value have already passed through the command + // stream. + // Returns: + // the value of the new token or -1 if the command buffer reader has + // shutdown. + int32 InsertToken(); + + // Waits until the token of a particular value has passed through the command + // stream (i.e. commands inserted before that token have been executed). + // NOTE: This will call Flush if it needs to block. + // Parameters: + // the value of the token to wait for. + void WaitForToken(int32 token); + + // Waits for a certain amount of space to be available. Returns address + // of space. + CommandBufferEntry* GetSpace(uint32 entries); + + // Typed version of GetSpace. Gets enough room for the given type and returns + // a reference to it. + template <typename T> + T& GetCmdSpace() { + COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed); + uint32 space_needed = ComputeNumEntries(sizeof(T)); + void* data = GetSpace(space_needed); + return *reinterpret_cast<T*>(data); + } + + // Typed version of GetSpace for immediate commands. + template <typename T> + T& GetImmediateCmdSpace(size_t space) { + COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); + uint32 space_needed = ComputeNumEntries(sizeof(T) + space); + void* data = GetSpace(space_needed); + return *reinterpret_cast<T*>(data); + } + + parse_error::ParseError GetParseError(); + + // Common Commands + void Noop(uint32 skip_count) { + cmd::Noop& cmd = GetImmediateCmdSpace<cmd::Noop>( + skip_count * sizeof(CommandBufferEntry)); + cmd.Init(skip_count); + } + + void SetToken(uint32 token) { + cmd::SetToken& cmd = GetCmdSpace<cmd::SetToken>(); + cmd.Init(token); + } + + + private: + // Waits until get changes, updating the value of get_. + void WaitForGetChange(); + + // Returns the number of available entries (they may not be contiguous). + int32 AvailableEntries() { + return (get_ - put_ - 1 + entry_count_) % entry_count_; + } + + NPP npp_; + gpu_plugin::NPObjectPointer<gpu_plugin::CommandBuffer> command_buffer_; + ::base::SharedMemory* ring_buffer_; + CommandBufferEntry *entries_; + int32 entry_count_; + int32 token_; + int32 last_token_read_; + int32 get_; + int32 put_; + + friend class CommandBufferHelperTest; + DISALLOW_COPY_AND_ASSIGN(CommandBufferHelper); +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_CLIENT_CROSS_CMD_BUFFER_HELPER_H_ diff --git a/o3d/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/o3d/gpu/command_buffer/client/cmd_buffer_helper_test.cc new file mode 100644 index 0000000..b2f5bde --- /dev/null +++ b/o3d/gpu/command_buffer/client/cmd_buffer_helper_test.cc @@ -0,0 +1,298 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// Tests for the Command Buffer Helper. + +#include "tests/common/win/testing_common.h" +#include "base/message_loop.h" +#include "gpu/command_buffer/client/cmd_buffer_helper.h" +#include "gpu/command_buffer/service/mocks.h" +#include "gpu/gpu_plugin/command_buffer.h" +#include "gpu/gpu_plugin/gpu_processor.h" +#include "gpu/np_utils/np_object_pointer.h" + +namespace command_buffer { + +using gpu_plugin::CommandBuffer; +using gpu_plugin::GPUProcessor; +using gpu_plugin::NPCreateObject; +using gpu_plugin::NPObjectPointer; +using testing::Return; +using testing::Mock; +using testing::Truly; +using testing::Sequence; +using testing::DoAll; +using testing::Invoke; +using testing::_; + +const int32 kNumCommandEntries = 10; +const int32 kCommandBufferSizeBytes = kNumCommandEntries * sizeof(int32); + +// Test fixture for CommandBufferHelper test - Creates a CommandBufferHelper, +// using a CommandBufferEngine with a mock AsyncAPIInterface for its interface +// (calling it directly, not through the RPC mechanism). +class CommandBufferHelperTest : public testing::Test { + protected: + virtual void SetUp() { + api_mock_.reset(new AsyncAPIMock); + // ignore noops in the mock - we don't want to inspect the internals of the + // helper. + EXPECT_CALL(*api_mock_, DoCommand(0, 0, _)) + .WillRepeatedly(Return(parse_error::kParseNoError)); + + ::base::SharedMemory* ring_buffer = new ::base::SharedMemory; + ring_buffer->Create(std::wstring(), false, false, kCommandBufferSizeBytes); + ring_buffer->Map(1024); + + command_buffer_ = NPCreateObject<CommandBuffer>(NULL); + command_buffer_->Initialize(ring_buffer); + + parser_ = new command_buffer::CommandParser(ring_buffer->memory(), + kCommandBufferSizeBytes, + 0, + kCommandBufferSizeBytes, + 0, + api_mock_.get()); + + scoped_refptr<GPUProcessor> gpu_processor(new GPUProcessor( + NULL, command_buffer_.Get(), NULL, NULL, parser_, 1)); + command_buffer_->SetPutOffsetChangeCallback(NewCallback( + gpu_processor.get(), &GPUProcessor::ProcessCommands)); + + api_mock_->set_engine(gpu_processor.get()); + + helper_.reset(new CommandBufferHelper(NULL, command_buffer_)); + helper_->Initialize(); + } + + virtual void TearDown() { + // If the GPUProcessor posts any tasks, this forces them to run. + MessageLoop::current()->RunAllPending(); + helper_.release(); + } + + // Adds a command to the buffer through the helper, while adding it as an + // expected call on the API mock. + void AddCommandWithExpect(parse_error::ParseError _return, + unsigned int command, + unsigned int arg_count, + CommandBufferEntry *args) { + helper_->AddCommand(command, arg_count, args); + EXPECT_CALL(*api_mock_, DoCommand(command, arg_count, + Truly(AsyncAPIMock::IsArgs(arg_count, args)))) + .InSequence(sequence_) + .WillOnce(Return(_return)); + } + + // Checks that the buffer from put to put+size is free in the parser. + void CheckFreeSpace(CommandBufferOffset put, unsigned int size) { + CommandBufferOffset parser_put = parser_->put(); + CommandBufferOffset parser_get = parser_->get(); + CommandBufferOffset limit = put + size; + if (parser_get > parser_put) { + // "busy" buffer wraps, so "free" buffer is between put (inclusive) and + // get (exclusive). + EXPECT_LE(parser_put, put); + EXPECT_GT(parser_get, limit); + } else { + // "busy" buffer does not wrap, so the "free" buffer is the top side (from + // put to the limit) and the bottom side (from 0 to get). + if (put >= parser_put) { + // we're on the top side, check we are below the limit. + EXPECT_GE(kNumCommandEntries, limit); + } else { + // we're on the bottom side, check we are below get. + EXPECT_GT(parser_get, limit); + } + } + } + + CommandBufferOffset get_helper_put() { return helper_->put_; } + + scoped_ptr<AsyncAPIMock> api_mock_; + NPObjectPointer<CommandBuffer> command_buffer_; + command_buffer::CommandParser* parser_; + scoped_ptr<CommandBufferHelper> helper_; + Sequence sequence_; +}; + +// Checks that commands in the buffer are properly executed, and that the +// status/error stay valid. +TEST_F(CommandBufferHelperTest, TestCommandProcessing) { + // Check initial state of the engine - it should have been configured by the + // helper. + EXPECT_TRUE(parser_ != NULL); + EXPECT_FALSE(command_buffer_->GetErrorStatus()); + EXPECT_EQ(parse_error::kParseNoError, command_buffer_->ResetParseError()); + EXPECT_EQ(0u, command_buffer_->GetGetOffset()); + + // Add 3 commands through the helper + AddCommandWithExpect(parse_error::kParseNoError, 1, 0, NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + AddCommandWithExpect(parse_error::kParseNoError, 2, 2, args1); + + CommandBufferEntry args2[2]; + args2[0].value_uint32 = 5; + args2[1].value_float = 6.f; + AddCommandWithExpect(parse_error::kParseNoError, 3, 2, args2); + + helper_->Flush(); + // Check that the engine has work to do now. + EXPECT_FALSE(parser_->IsEmpty()); + + // Wait until it's done. + helper_->Finish(); + // Check that the engine has no more work to do. + EXPECT_TRUE(parser_->IsEmpty()); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_FALSE(command_buffer_->GetErrorStatus()); + EXPECT_EQ(parse_error::kParseNoError, command_buffer_->ResetParseError()); +} + +// Checks that commands in the buffer are properly executed when wrapping the +// buffer, and that the status/error stay valid. +TEST_F(CommandBufferHelperTest, TestCommandWrapping) { + // Add 5 commands of size 3 through the helper to make sure we do wrap. + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + + for (unsigned int i = 0; i < 5; ++i) { + AddCommandWithExpect(parse_error::kParseNoError, i + 1, 2, args1); + } + + helper_->Finish(); + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_FALSE(command_buffer_->GetErrorStatus()); + EXPECT_EQ(parse_error::kParseNoError, command_buffer_->ResetParseError()); +} + + +// Checks that commands in the buffer are properly executed, even if they +// generate a recoverable error. Check that the error status is properly set, +// and reset when queried. +TEST_F(CommandBufferHelperTest, TestRecoverableError) { + CommandBufferEntry args[2]; + args[0].value_uint32 = 3; + args[1].value_float = 4.f; + + // Create a command buffer with 3 commands, 2 of them generating errors + AddCommandWithExpect(parse_error::kParseNoError, 1, 2, args); + AddCommandWithExpect(parse_error::kParseUnknownCommand, 2, 2, args); + AddCommandWithExpect(parse_error::kParseInvalidArguments, 3, 2, + args); + + helper_->Finish(); + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check that the error status was set to the first error. + EXPECT_EQ(parse_error::kParseUnknownCommand, + command_buffer_->ResetParseError()); + // Check that the error status was reset after the query. + EXPECT_EQ(parse_error::kParseNoError, command_buffer_->ResetParseError()); +} + +// Checks that asking for available entries work, and that the parser +// effectively won't use that space. +TEST_F(CommandBufferHelperTest, TestAvailableEntries) { + CommandBufferEntry args[2]; + args[0].value_uint32 = 3; + args[1].value_float = 4.f; + + // Add 2 commands through the helper - 8 entries + AddCommandWithExpect(parse_error::kParseNoError, 1, 0, NULL); + AddCommandWithExpect(parse_error::kParseNoError, 2, 0, NULL); + AddCommandWithExpect(parse_error::kParseNoError, 3, 2, args); + AddCommandWithExpect(parse_error::kParseNoError, 4, 2, args); + + // Ask for 5 entries. + helper_->WaitForAvailableEntries(5); + + CommandBufferOffset put = get_helper_put(); + CheckFreeSpace(put, 5); + + // Add more commands. + AddCommandWithExpect(parse_error::kParseNoError, 5, 2, args); + + // Wait until everything is done done. + helper_->Finish(); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_FALSE(command_buffer_->GetErrorStatus()); + EXPECT_EQ(parse_error::kParseNoError, command_buffer_->ResetParseError()); +} + +// Checks that the InsertToken/WaitForToken work. +TEST_F(CommandBufferHelperTest, TestToken) { + CommandBufferEntry args[2]; + args[0].value_uint32 = 3; + args[1].value_float = 4.f; + + // Add a first command. + AddCommandWithExpect(parse_error::kParseNoError, 3, 2, args); + // keep track of the buffer position. + CommandBufferOffset command1_put = get_helper_put(); + int32 token = helper_->InsertToken(); + + EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) + .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), + Return(parse_error::kParseNoError))); + // Add another command. + AddCommandWithExpect(parse_error::kParseNoError, 4, 2, args); + helper_->WaitForToken(token); + // check that the get pointer is beyond the first command. + EXPECT_LE(command1_put, command_buffer_->GetGetOffset()); + helper_->Finish(); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_FALSE(command_buffer_->GetErrorStatus()); + EXPECT_EQ(parse_error::kParseNoError, command_buffer_->ResetParseError()); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/client/effect_helper.cc b/o3d/gpu/command_buffer/client/effect_helper.cc new file mode 100644 index 0000000..f3daaae --- /dev/null +++ b/o3d/gpu/command_buffer/client/effect_helper.cc @@ -0,0 +1,250 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the EffectHelper class. + +#include "gpu/command_buffer/common/o3d_cmd_format.h" +#include "gpu/command_buffer/client/cmd_buffer_helper.h" +#include "gpu/command_buffer/client/effect_helper.h" +#include "gpu/command_buffer/client/fenced_allocator.h" +#include "gpu/command_buffer/client/id_allocator.h" + +// TODO: write a unit test. + +namespace command_buffer { + +bool EffectHelper::CreateEffectParameters(ResourceId effect_id, + std::vector<EffectParamDesc> *descs) { + using effect_param::Desc; + DCHECK_NE(effect_id, kInvalidResource); + DCHECK(descs); + descs->clear(); + + // Get the param count. + Uint32 *retval = shm_allocator_->AllocTyped<Uint32>(1); + helper_->GetParamCount(effect_id, sizeof(*retval), + shm_id_, shm_allocator_->GetOffset(retval)); + // Finish has to be called to get the result. + helper_->Finish(); + + // We could have failed if the effect_id is invalid. + if (helper_->GetParseError() != parse_error::kParseNoError) { + shm_allocator_->Free(retval); + return false; + } + unsigned int param_count = *retval; + + shm_allocator_->Free(retval); + unsigned int max_buffer_size = shm_allocator_->GetLargestFreeOrPendingSize(); + if (max_buffer_size < sizeof(Desc)) { // NOLINT + // Not enough memory to get at least 1 param desc. + return false; + } + descs->resize(param_count); + for (unsigned int i = 0; i < param_count; ++i) { + EffectParamDesc *desc = &((*descs)[i]); + desc->id = param_id_allocator_->AllocateID(); + helper_->CreateParam(desc->id, effect_id, i); + } + + // Read param descriptions in batches. We use as much shared memory as + // possible so that we only call Finish as little as possible. + unsigned int max_param_per_batch = + std::min(static_cast<unsigned>(param_count), + static_cast<unsigned>(max_buffer_size / sizeof(Desc))); // NOLINT + Desc *raw_descs = shm_allocator_->AllocTyped<Desc>(max_param_per_batch); + DCHECK(raw_descs); + for (unsigned int i = 0; i < param_count; i += max_param_per_batch) { + unsigned int count = std::min(param_count - i, max_param_per_batch); + for (unsigned int j = 0 ; j < count; ++j) { + EffectParamDesc *desc = &((*descs)[i + j]); + Desc *raw_desc = raw_descs + j; + helper_->GetParamDesc(desc->id, sizeof(*raw_desc), + shm_id_, + shm_allocator_->GetOffset(raw_desc)); + } + // Finish to get the results. + helper_->Finish(); + DCHECK_EQ(helper_->GetParseError(), parse_error::kParseNoError); + for (unsigned int j = 0 ; j < count; ++j) { + EffectParamDesc *desc = &((*descs)[i + j]); + Desc *raw_desc = raw_descs + j; + desc->data_type = raw_desc->data_type; + desc->data_size = raw_desc->data_size; + desc->num_elements = raw_desc->num_elements; + desc->cmd_desc_size = raw_desc->size; + } + } + shm_allocator_->Free(raw_descs); + return true; +} + +bool EffectHelper::GetParamStrings(EffectParamDesc *desc) { + using effect_param::Desc; + DCHECK(desc); + DCHECK_NE(desc->id, kInvalidResource); + // desc may not have come directly from CreateEffectParameters, so it may be + // less than the minimum required size. + unsigned int size = std::max(static_cast<unsigned>(desc->cmd_desc_size), + static_cast<unsigned>(sizeof(Desc))); // NOLINT + Desc *raw_desc = static_cast<Desc *>(shm_allocator_->Alloc(size)); + if (!raw_desc) { + // Not enough memory to get the param desc. + return false; + } + helper_->GetParamDesc(desc->id, size, + shm_id_, + shm_allocator_->GetOffset(raw_desc)); + + // Finish to get the results. + helper_->Finish(); + + // We could have failed if the param ID is invalid. + if (helper_->GetParseError() != parse_error::kParseNoError) { + shm_allocator_->Free(raw_desc); + return false; + } + + if (raw_desc->size > size) { + // We had not allocated enough memory the first time (e.g. if the + // EffectParamDesc didn't come from CreateEffectParameters, so the user had + // no way of knowing what size was needed for the strings), so re-allocate + // and try again. + size = raw_desc->size; + desc->cmd_desc_size = size; + shm_allocator_->Free(raw_desc); + raw_desc = static_cast<Desc *>(shm_allocator_->Alloc(size)); + if (!raw_desc) { + // Not enough memory to get the param desc. + return false; + } + helper_->GetParamDesc(desc->id, size, + shm_id_, + shm_allocator_->GetOffset(raw_desc)); + // Finish to get the results. + helper_->Finish(); + DCHECK_EQ(helper_->GetParseError(), parse_error::kParseNoError); + DCHECK_EQ(raw_desc->size, size); + } + + const char *raw_desc_string = reinterpret_cast<char *>(raw_desc); + if (raw_desc->name_offset) { + DCHECK_LE(raw_desc->name_offset + raw_desc->name_size, raw_desc->size); + DCHECK_GT(raw_desc->name_size, 0U); + DCHECK_EQ(raw_desc_string[raw_desc->name_offset + raw_desc->name_size - 1], + 0); + desc->name = String(raw_desc_string + raw_desc->name_offset, + raw_desc->name_size - 1); + } else { + desc->name.clear(); + } + if (raw_desc->semantic_offset) { + DCHECK_LE(raw_desc->semantic_offset + raw_desc->semantic_size, + raw_desc->size); + DCHECK_GT(raw_desc->semantic_size, 0U); + DCHECK_EQ(raw_desc_string[raw_desc->semantic_offset + + raw_desc->semantic_size - 1], + 0); + desc->semantic = String(raw_desc_string + raw_desc->semantic_offset, + raw_desc->semantic_size - 1); + } else { + desc->semantic.clear(); + } + shm_allocator_->Free(raw_desc); + return true; +} + +void EffectHelper::DestroyEffectParameters( + const std::vector<EffectParamDesc> &descs) { + for (unsigned int i = 0; i < descs.size(); ++i) { + const EffectParamDesc &desc = descs[i]; + helper_->DestroyParam(desc.id); + param_id_allocator_->FreeID(desc.id); + } +} + +bool EffectHelper::GetEffectStreams(ResourceId effect_id, + std::vector<EffectStreamDesc> *descs) { + using effect_stream::Desc; + DCHECK_NE(effect_id, kInvalidResource); + + // Get the param count. + Uint32 *retval = shm_allocator_->AllocTyped<Uint32>(1); + helper_->GetStreamCount(effect_id, sizeof(*retval), + shm_id_, + shm_allocator_->GetOffset(retval)); + // Finish has to be called to get the result. + helper_->Finish(); + + // We could have failed if the effect_id is invalid. + if (helper_->GetParseError() != parse_error::kParseNoError) { + shm_allocator_->Free(retval); + return false; + } + unsigned int stream_count = *retval; + shm_allocator_->Free(retval); + unsigned int max_buffer_size = shm_allocator_->GetLargestFreeOrPendingSize(); + if (max_buffer_size < sizeof(Desc)) { // NOLINT + // Not enough memory to get at least 1 stream desc. + return false; + } + descs->resize(stream_count); + + // Read stream descriptions in batches. We use as much shared memory as + // possible so that we only call Finish as little as possible. + unsigned int max_stream_per_batch = + std::min(static_cast<unsigned>(stream_count), + static_cast<unsigned>(max_buffer_size / sizeof(Desc))); // NOLINT + Desc *raw_descs = shm_allocator_->AllocTyped<Desc>(max_stream_per_batch); + DCHECK(raw_descs); + for (unsigned int i = 0; i < stream_count; i += max_stream_per_batch) { + unsigned int count = std::min(stream_count - i, max_stream_per_batch); + for (unsigned int j = 0 ; j < count; ++j) { + Desc *raw_desc = raw_descs + j; + helper_->GetStreamDesc(effect_id, i + j, sizeof(*raw_desc), + shm_id_, + shm_allocator_->GetOffset(raw_desc)); + } + // Finish to get the results. + helper_->Finish(); + DCHECK_EQ(helper_->GetParseError(), parse_error::kParseNoError); + for (unsigned int j = 0 ; j < count; ++j) { + EffectStreamDesc *desc = &((*descs)[i + j]); + Desc *raw_desc = raw_descs + j; + desc->semantic = static_cast<vertex_struct::Semantic>(raw_desc->semantic); + desc->semantic_index = raw_desc->semantic_index; + } + } + shm_allocator_->Free(raw_descs); + return true; +} +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/client/effect_helper.h b/o3d/gpu/command_buffer/client/effect_helper.h new file mode 100644 index 0000000..dc11b24 --- /dev/null +++ b/o3d/gpu/command_buffer/client/effect_helper.h @@ -0,0 +1,156 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file defines the EffectHelper class. + +#ifndef GPU_COMMAND_BUFFER_CLIENT_CROSS_EFFECT_HELPER_H_ +#define GPU_COMMAND_BUFFER_CLIENT_CROSS_EFFECT_HELPER_H_ + +#include <vector> +#include "gpu/command_buffer/common/resource.h" +#include "gpu/command_buffer/client/o3d_cmd_helper.h" + +namespace command_buffer { + +class FencedAllocatorWrapper; +class IdAllocator; +class CommandBufferHelper; + +// A helper class to find parameters in an effect. +class EffectHelper { + public: + // A more usable version of effect_param::Desc + struct EffectParamDesc { + ResourceId id; // The resource ID for the param. + String name; // The name of the param. + String semantic; // The semantic of the param. + effect_param::DataType data_type; // The data type of a param. + unsigned int data_size; // The size of the data for a param. + int num_elements; // The number of array entries if the + // parameter is an array, 0 otherwise. + unsigned int cmd_desc_size; // The size of the effect_param::Desc + // structure (counting strings) for a + // param. + }; + struct EffectStreamDesc { + vertex_struct::Semantic semantic; // The semantic enum type. + unsigned int semantic_index; + }; + + EffectHelper(O3DCmdHelper *helper, + FencedAllocatorWrapper *shm_allocator, + unsigned int shm_id, + IdAllocator *param_id_allocator) + : helper_(helper), + shm_allocator_(shm_allocator), + shm_id_(shm_id), + param_id_allocator_(param_id_allocator) { + DCHECK(helper); + DCHECK(shm_allocator); + DCHECK(param_id_allocator); + } + + // Creates all the parameters in an effect and gets their descriptions. The + // strings will not be retrieved, so name and semantic will be empty. The + // cmd_desc_size field will be set to the proper size to be able to get the + // strings with a single command within GetParamStrings, so it should be left + // alone. + // + // The ResourceIDs will be allocated in the param_id_allocator. + // Temporary buffers will be allocated in the shm_allocator, but they will be + // freed before the function returns (possibly pending a token). At least + // sizeof(effect_param::Desc) must be available for this function to succeed. + // This function will call Finish(), hence will block. + // + // Parameters: + // effect_id: the ResourceId of the effect. + // descs: A pointer to a vector containing the returned descriptions. + // The pointed vector will be cleared. + // Returns: + // true if successful. Reasons for failure are: + // - invalid effect_id, + // - not enough memory in the shm_allocator_. + bool CreateEffectParameters(ResourceId effect_id, + std::vector<EffectParamDesc> *descs); + + // Gets the strings for a desc. This will fill in the values for the name and + // semantic fields. + // Temporary buffers will be allocated in the shm_allocator, but they will be + // freed before the function returns (possibly pending a token). At least + // desc.cmd_desc_size (as returned by CreateEffectParameters) must be + // available for this function to succeed. + // This function will call Finish(), hence will block. + // + // Parameters: + // desc: a pointer to the description for a parameter. The id field should + // be set to the ResourceId of the parameter. + // Returns: + // true if successful. Reasons for failure are: + // - invalid parameter ResourceId, + // - not enough memory in the shm_allocator_. + bool GetParamStrings(EffectParamDesc *desc); + + // Destroys all parameter resources referenced by the descriptions. The + // ResourceId will be freed from the param_id_allocator. + // Parameters: + // descs: the vector of descriptions containing the ResourceIDs of the + // parameters to destroy. + void DestroyEffectParameters(const std::vector<EffectParamDesc> &descs); + + // Gets all the input stream semantics and semantic indices in an + // array. These will be retrieved as many as possible at a time. At least + // sizeof(effect_param::Desc) must be available for this function to succeed. + // This function will call Finish(), hence will block. + // + // Parameters: + // effect_id: the ResourceId of the effect. + // descs: A pointer to a vector containing the returned descriptions. + // The pointed vector will be cleared. + // Returns: + // true if successful. Reasons for failure are: + // - invalid effect_id, + // - not enough memory in the shm_allocator_. + bool GetEffectStreams(ResourceId effect_id, + std::vector<EffectStreamDesc> *descs); + + private: + O3DCmdHelper *helper_; + FencedAllocatorWrapper *shm_allocator_; + unsigned int shm_id_; + IdAllocator *param_id_allocator_; + + DISALLOW_COPY_AND_ASSIGN(EffectHelper); +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_CLIENT_CROSS_EFFECT_HELPER_H_ diff --git a/o3d/gpu/command_buffer/client/fenced_allocator.cc b/o3d/gpu/command_buffer/client/fenced_allocator.cc new file mode 100644 index 0000000..810feb5 --- /dev/null +++ b/o3d/gpu/command_buffer/client/fenced_allocator.cc @@ -0,0 +1,214 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the FencedAllocator class. + +#include "gpu/command_buffer/client/fenced_allocator.h" +#include <algorithm> +#include "gpu/command_buffer/client/cmd_buffer_helper.h" + +namespace command_buffer { + +#ifndef COMPILER_MSVC +const FencedAllocator::Offset FencedAllocator::kInvalidOffset; +#endif + +FencedAllocator::~FencedAllocator() { + // Free blocks pending tokens. + for (unsigned int i = 0; i < blocks_.size(); ++i) { + if (blocks_[i].state == FREE_PENDING_TOKEN) { + i = WaitForTokenAndFreeBlock(i); + } + } + DCHECK_EQ(blocks_.size(), 1u); + DCHECK_EQ(blocks_[0].state, FREE); +} + +// Looks for a non-allocated block that is big enough. Search in the FREE +// blocks first (for direct usage), first-fit, then in the FREE_PENDING_TOKEN +// blocks, waiting for them. The current implementation isn't smart about +// optimizing what to wait for, just looks inside the block in order (first-fit +// as well). +FencedAllocator::Offset FencedAllocator::Alloc(unsigned int size) { + // Similarly to malloc, an allocation of 0 allocates at least 1 byte, to + // return different pointers every time. + if (size == 0) size = 1; + + // Try first to allocate in a free block. + for (unsigned int i = 0; i < blocks_.size(); ++i) { + Block &block = blocks_[i]; + if (block.state == FREE && block.size >= size) { + return AllocInBlock(i, size); + } + } + + // No free block is available. Look for blocks pending tokens, and wait for + // them to be re-usable. + for (unsigned int i = 0; i < blocks_.size(); ++i) { + if (blocks_[i].state != FREE_PENDING_TOKEN) + continue; + i = WaitForTokenAndFreeBlock(i); + if (blocks_[i].size >= size) + return AllocInBlock(i, size); + } + return kInvalidOffset; +} + +// Looks for the corresponding block, mark it FREE, and collapse it if +// necessary. +void FencedAllocator::Free(FencedAllocator::Offset offset) { + BlockIndex index = GetBlockByOffset(offset); + DCHECK_NE(blocks_[index].state, FREE); + blocks_[index].state = FREE; + CollapseFreeBlock(index); +} + +// Looks for the corresponding block, mark it FREE_PENDING_TOKEN. +void FencedAllocator::FreePendingToken(FencedAllocator::Offset offset, + unsigned int token) { + BlockIndex index = GetBlockByOffset(offset); + Block &block = blocks_[index]; + block.state = FREE_PENDING_TOKEN; + block.token = token; +} + +// Gets the max of the size of the blocks marked as free. +unsigned int FencedAllocator::GetLargestFreeSize() { + unsigned int max_size = 0; + for (unsigned int i = 0; i < blocks_.size(); ++i) { + Block &block = blocks_[i]; + if (block.state == FREE) + max_size = std::max(max_size, block.size); + } + return max_size; +} + +// Gets the size of the largest segment of blocks that are either FREE or +// FREE_PENDING_TOKEN. +unsigned int FencedAllocator::GetLargestFreeOrPendingSize() { + unsigned int max_size = 0; + unsigned int current_size = 0; + for (unsigned int i = 0; i < blocks_.size(); ++i) { + Block &block = blocks_[i]; + if (block.state == IN_USE) { + max_size = std::max(max_size, current_size); + current_size = 0; + } else { + DCHECK(block.state == FREE || block.state == FREE_PENDING_TOKEN); + current_size += block.size; + } + } + return std::max(max_size, current_size); +} + +// Makes sure that: +// - there is at least one block. +// - there are no contiguous FREE blocks (they should have been collapsed). +// - the successive offsets match the block sizes, and they are in order. +bool FencedAllocator::CheckConsistency() { + if (blocks_.size() < 1) return false; + for (unsigned int i = 0; i < blocks_.size() - 1; ++i) { + Block ¤t = blocks_[i]; + Block &next = blocks_[i + 1]; + // This test is NOT included in the next one, because offset is unsigned. + if (next.offset <= current.offset) + return false; + if (next.offset != current.offset + current.size) + return false; + if (current.state == FREE && next.state == FREE) + return false; + } + return true; +} + +// Collapse the block to the next one, then to the previous one. Provided the +// structure is consistent, those are the only blocks eligible for collapse. +FencedAllocator::BlockIndex FencedAllocator::CollapseFreeBlock( + BlockIndex index) { + if (index + 1 < blocks_.size()) { + Block &next = blocks_[index + 1]; + if (next.state == FREE) { + blocks_[index].size += next.size; + blocks_.erase(blocks_.begin() + index + 1); + } + } + if (index > 0) { + Block &prev = blocks_[index - 1]; + if (prev.state == FREE) { + prev.size += blocks_[index].size; + blocks_.erase(blocks_.begin() + index); + --index; + } + } + return index; +} + +// Waits for the block's token, then mark the block as free, then collapse it. +FencedAllocator::BlockIndex FencedAllocator::WaitForTokenAndFreeBlock( + BlockIndex index) { + Block &block = blocks_[index]; + DCHECK_EQ(block.state, FREE_PENDING_TOKEN); + helper_->WaitForToken(block.token); + block.state = FREE; + return CollapseFreeBlock(index); +} + +// If the block is exactly the requested size, simply mark it IN_USE, otherwise +// split it and mark the first one (of the requested size) IN_USE. +FencedAllocator::Offset FencedAllocator::AllocInBlock(BlockIndex index, + unsigned int size) { + Block &block = blocks_[index]; + DCHECK_GE(block.size, size); + DCHECK_EQ(block.state, FREE); + Offset offset = block.offset; + if (block.size == size) { + block.state = IN_USE; + return offset; + } + Block newblock = { FREE, offset + size, block.size - size, kUnusedToken}; + block.state = IN_USE; + block.size = size; + // this is the last thing being done because it may invalidate block; + blocks_.insert(blocks_.begin() + index + 1, newblock); + return offset; +} + +// The blocks are in offset order, so we can do a binary search. +FencedAllocator::BlockIndex FencedAllocator::GetBlockByOffset(Offset offset) { + Block templ = { IN_USE, offset, 0, kUnusedToken }; + Container::iterator it = std::lower_bound(blocks_.begin(), blocks_.end(), + templ, OffsetCmp()); + DCHECK(it != blocks_.end() && it->offset == offset); + return it-blocks_.begin(); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/client/fenced_allocator.h b/o3d/gpu/command_buffer/client/fenced_allocator.h new file mode 100644 index 0000000..72bba33 --- /dev/null +++ b/o3d/gpu/command_buffer/client/fenced_allocator.h @@ -0,0 +1,266 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the FencedAllocator class. + +#ifndef GPU_COMMAND_BUFFER_CLIENT_CROSS_FENCED_ALLOCATOR_H_ +#define GPU_COMMAND_BUFFER_CLIENT_CROSS_FENCED_ALLOCATOR_H_ + +#include <vector> +#include "base/basictypes.h" +#include "gpu/command_buffer/common/logging.h" + +namespace command_buffer { +class CommandBufferHelper; + +// FencedAllocator provides a mechanism to manage allocations within a fixed +// block of memory (storing the book-keeping externally). Furthermore this +// class allows to free data "pending" the passage of a command buffer token, +// that is, the memory won't be reused until the command buffer has processed +// that token. +// +// NOTE: Although this class is intended to be used in the command buffer +// environment which is multi-process, this class isn't "thread safe", because +// it isn't meant to be shared across modules. It is thread-compatible though +// (see http://www.corp.google.com/eng/doc/cpp_primer.html#thread_safety). +class FencedAllocator { + public: + typedef unsigned int Offset; + // Invalid offset, returned by Alloc in case of failure. + static const Offset kInvalidOffset = 0xffffffffU; + + // Creates a FencedAllocator. Note that the size of the buffer is passed, but + // not its base address: everything is handled as offsets into the buffer. + FencedAllocator(unsigned int size, + CommandBufferHelper *helper) + : helper_(helper) { + Block block = { FREE, 0, size, kUnusedToken }; + blocks_.push_back(block); + } + + ~FencedAllocator(); + + // Allocates a block of memory. If the buffer is out of directly available + // memory, this function may wait until memory that was freed "pending a + // token" can be re-used. + // + // Parameters: + // size: the size of the memory block to allocate. + // + // Returns: + // the offset of the allocated memory block, or kInvalidOffset if out of + // memory. + Offset Alloc(unsigned int size); + + // Frees a block of memory. + // + // Parameters: + // offset: the offset of the memory block to free. + void Free(Offset offset); + + // Frees a block of memory, pending the passage of a token. That memory won't + // be re-allocated until the token has passed through the command stream. + // + // Parameters: + // offset: the offset of the memory block to free. + // token: the token value to wait for before re-using the memory. + void FreePendingToken(Offset offset, unsigned int token); + + // Gets the size of the largest free block that is available without waiting. + unsigned int GetLargestFreeSize(); + + // Gets the size of the largest free block that can be allocated if the + // caller can wait. Allocating a block of this size will succeed, but may + // block. + unsigned int GetLargestFreeOrPendingSize(); + + // Checks for consistency inside the book-keeping structures. Used for + // testing. + bool CheckConsistency(); + + private: + // Status of a block of memory, for book-keeping. + enum State { + IN_USE, + FREE, + FREE_PENDING_TOKEN + }; + + // Book-keeping sturcture that describes a block of memory. + struct Block { + State state; + Offset offset; + unsigned int size; + unsigned int token; // token to wait for in the FREE_PENDING_TOKEN case. + }; + + // Comparison functor for memory block sorting. + class OffsetCmp { + public: + bool operator() (const Block &left, const Block &right) { + return left.offset < right.offset; + } + }; + + typedef std::vector<Block> Container; + typedef unsigned int BlockIndex; + + static const unsigned int kUnusedToken = 0; + + // Gets the index of a memory block, given its offset. + BlockIndex GetBlockByOffset(Offset offset); + + // Collapse a free block with its neighbours if they are free. Returns the + // index of the collapsed block. + // NOTE: this will invalidate block indices. + BlockIndex CollapseFreeBlock(BlockIndex index); + + // Waits for a FREE_PENDING_TOKEN block to be usable, and free it. Returns + // the new index of that block (since it may have been collapsed). + // NOTE: this will invalidate block indices. + BlockIndex WaitForTokenAndFreeBlock(BlockIndex index); + + // Allocates a block of memory inside a given block, splitting it in two + // (unless that block is of the exact requested size). + // NOTE: this will invalidate block indices. + // Returns the offset of the allocated block (NOTE: this is different from + // the other functions that return a block index). + Offset AllocInBlock(BlockIndex index, unsigned int size); + + command_buffer::CommandBufferHelper *helper_; + Container blocks_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(FencedAllocator); +}; + +// This class functions just like FencedAllocator, but its API uses pointers +// instead of offsets. +class FencedAllocatorWrapper { + public: + FencedAllocatorWrapper(unsigned int size, + CommandBufferHelper *helper, + void *base) + : allocator_(size, helper), + base_(base) { } + + // Allocates a block of memory. If the buffer is out of directly available + // memory, this function may wait until memory that was freed "pending a + // token" can be re-used. + // + // Parameters: + // size: the size of the memory block to allocate. + // + // Returns: + // the pointer to the allocated memory block, or NULL if out of + // memory. + void *Alloc(unsigned int size) { + FencedAllocator::Offset offset = allocator_.Alloc(size); + return GetPointer(offset); + } + + // Allocates a block of memory. If the buffer is out of directly available + // memory, this function may wait until memory that was freed "pending a + // token" can be re-used. + // This is a type-safe version of Alloc, returning a typed pointer. + // + // Parameters: + // count: the number of elements to allocate. + // + // Returns: + // the pointer to the allocated memory block, or NULL if out of + // memory. + template <typename T> T *AllocTyped(unsigned int count) { + return static_cast<T *>(Alloc(count * sizeof(T))); + } + + // Frees a block of memory. + // + // Parameters: + // pointer: the pointer to the memory block to free. + void Free(void *pointer) { + DCHECK(pointer); + allocator_.Free(GetOffset(pointer)); + } + + // Frees a block of memory, pending the passage of a token. That memory won't + // be re-allocated until the token has passed through the command stream. + // + // Parameters: + // pointer: the pointer to the memory block to free. + // token: the token value to wait for before re-using the memory. + void FreePendingToken(void *pointer, unsigned int token) { + DCHECK(pointer); + allocator_.FreePendingToken(GetOffset(pointer), token); + } + + // Gets a pointer to a memory block given the base memory and the offset. + // It translates FencedAllocator::kInvalidOffset to NULL. + void *GetPointer(FencedAllocator::Offset offset) { + return (offset == FencedAllocator::kInvalidOffset) ? + NULL : static_cast<char *>(base_) + offset; + } + + // Gets the offset to a memory block given the base memory and the address. + // It translates NULL to FencedAllocator::kInvalidOffset. + FencedAllocator::Offset GetOffset(void *pointer) { + return pointer ? static_cast<char *>(pointer) - static_cast<char *>(base_) : + FencedAllocator::kInvalidOffset; + } + + // Gets the size of the largest free block that is available without waiting. + unsigned int GetLargestFreeSize() { + return allocator_.GetLargestFreeSize(); + } + + // Gets the size of the largest free block that can be allocated if the + // caller can wait. + unsigned int GetLargestFreeOrPendingSize() { + return allocator_.GetLargestFreeOrPendingSize(); + } + + // Checks for consistency inside the book-keeping structures. Used for + // testing. + bool CheckConsistency() { + return allocator_.CheckConsistency(); + } + + FencedAllocator &allocator() { return allocator_; } + + private: + FencedAllocator allocator_; + void *base_; + DISALLOW_IMPLICIT_CONSTRUCTORS(FencedAllocatorWrapper); +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_CLIENT_CROSS_FENCED_ALLOCATOR_H_ diff --git a/o3d/gpu/command_buffer/client/fenced_allocator_test.cc b/o3d/gpu/command_buffer/client/fenced_allocator_test.cc new file mode 100644 index 0000000..ce76542 --- /dev/null +++ b/o3d/gpu/command_buffer/client/fenced_allocator_test.cc @@ -0,0 +1,496 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the tests for the FencedAllocator class. + +#include "tests/common/win/testing_common.h" +#include "base/message_loop.h" +#include "gpu/command_buffer/client/cmd_buffer_helper.h" +#include "gpu/command_buffer/client/fenced_allocator.h" +#include "gpu/command_buffer/service/cmd_buffer_engine.h" +#include "gpu/command_buffer/service/mocks.h" +#include "gpu/gpu_plugin/command_buffer.h" +#include "gpu/gpu_plugin/gpu_processor.h" +#include "gpu/np_utils/np_object_pointer.h" + +namespace command_buffer { + +using gpu_plugin::CommandBuffer; +using gpu_plugin::GPUProcessor; +using gpu_plugin::NPCreateObject; +using gpu_plugin::NPObjectPointer; +using testing::Return; +using testing::Mock; +using testing::Truly; +using testing::Sequence; +using testing::DoAll; +using testing::Invoke; +using testing::_; + +class BaseFencedAllocatorTest : public testing::Test { + protected: + static const unsigned int kBufferSize = 1024; + + virtual void SetUp() { + api_mock_.reset(new AsyncAPIMock); + // ignore noops in the mock - we don't want to inspect the internals of the + // helper. + EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, 0, _)) + .WillRepeatedly(Return(parse_error::kParseNoError)); + // Forward the SetToken calls to the engine + EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) + .WillRepeatedly(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), + Return(parse_error::kParseNoError))); + + ::base::SharedMemory* ring_buffer = new ::base::SharedMemory; + ring_buffer->Create(std::wstring(), false, false, 1024); + ring_buffer->Map(1024); + + command_buffer_ = NPCreateObject<CommandBuffer>(NULL); + command_buffer_->Initialize(ring_buffer); + + parser_ = new command_buffer::CommandParser(ring_buffer->memory(), + kBufferSize, + 0, + kBufferSize, + 0, + api_mock_.get()); + + scoped_refptr<GPUProcessor> gpu_processor(new GPUProcessor( + NULL, command_buffer_.Get(), NULL, NULL, parser_, INT_MAX)); + command_buffer_->SetPutOffsetChangeCallback(NewCallback( + gpu_processor.get(), &GPUProcessor::ProcessCommands)); + + api_mock_->set_engine(gpu_processor.get()); + + helper_.reset(new CommandBufferHelper(NULL, command_buffer_)); + helper_->Initialize(); + } + + virtual void TearDown() { + helper_.release(); + } + + scoped_ptr<AsyncAPIMock> api_mock_; + NPObjectPointer<CommandBuffer> command_buffer_; + command_buffer::CommandParser* parser_; + scoped_ptr<CommandBufferHelper> helper_; +}; + +#ifndef COMPILER_MSVC +const unsigned int BaseFencedAllocatorTest::kBufferSize; +#endif + +// Test fixture for FencedAllocator test - Creates a FencedAllocator, using a +// CommandBufferHelper with a mock AsyncAPIInterface for its interface (calling +// it directly, not through the RPC mechanism), making sure Noops are ignored +// and SetToken are properly forwarded to the engine. +class FencedAllocatorTest : public BaseFencedAllocatorTest { + protected: + virtual void SetUp() { + BaseFencedAllocatorTest::SetUp(); + allocator_.reset(new FencedAllocator(kBufferSize, helper_.get())); + } + + virtual void TearDown() { + // If the GPUProcessor posts any tasks, this forces them to run. + MessageLoop::current()->RunAllPending(); + + EXPECT_TRUE(allocator_->CheckConsistency()); + allocator_.release(); + + BaseFencedAllocatorTest::TearDown(); + } + + scoped_ptr<FencedAllocator> allocator_; +}; + +// Checks basic alloc and free. +TEST_F(FencedAllocatorTest, TestBasic) { + allocator_->CheckConsistency(); + + const unsigned int kSize = 16; + FencedAllocator::Offset offset = allocator_->Alloc(kSize); + EXPECT_NE(FencedAllocator::kInvalidOffset, offset); + EXPECT_GE(kBufferSize, offset+kSize); + EXPECT_TRUE(allocator_->CheckConsistency()); + + allocator_->Free(offset); + EXPECT_TRUE(allocator_->CheckConsistency()); +} + +// Checks out-of-memory condition. +TEST_F(FencedAllocatorTest, TestOutOfMemory) { + EXPECT_TRUE(allocator_->CheckConsistency()); + + const unsigned int kSize = 16; + const unsigned int kAllocCount = kBufferSize / kSize; + CHECK(kAllocCount * kSize == kBufferSize); + + // Allocate several buffers to fill in the memory. + FencedAllocator::Offset offsets[kAllocCount]; + for (unsigned int i = 0; i < kAllocCount; ++i) { + offsets[i] = allocator_->Alloc(kSize); + EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[i]); + EXPECT_GE(kBufferSize, offsets[i]+kSize); + EXPECT_TRUE(allocator_->CheckConsistency()); + } + + // This allocation should fail. + FencedAllocator::Offset offset_failed = allocator_->Alloc(kSize); + EXPECT_EQ(FencedAllocator::kInvalidOffset, offset_failed); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // Free one successful allocation, reallocate with half the size + allocator_->Free(offsets[0]); + EXPECT_TRUE(allocator_->CheckConsistency()); + offsets[0] = allocator_->Alloc(kSize/2); + EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[0]); + EXPECT_GE(kBufferSize, offsets[0]+kSize); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // This allocation should fail as well. + offset_failed = allocator_->Alloc(kSize); + EXPECT_EQ(FencedAllocator::kInvalidOffset, offset_failed); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // Free up everything. + for (unsigned int i = 0; i < kAllocCount; ++i) { + allocator_->Free(offsets[i]); + EXPECT_TRUE(allocator_->CheckConsistency()); + } +} + +// Checks the free-pending-token mechanism. +TEST_F(FencedAllocatorTest, TestFreePendingToken) { + EXPECT_TRUE(allocator_->CheckConsistency()); + + const unsigned int kSize = 16; + const unsigned int kAllocCount = kBufferSize / kSize; + CHECK(kAllocCount * kSize == kBufferSize); + + // Allocate several buffers to fill in the memory. + FencedAllocator::Offset offsets[kAllocCount]; + for (unsigned int i = 0; i < kAllocCount; ++i) { + offsets[i] = allocator_->Alloc(kSize); + EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[i]); + EXPECT_GE(kBufferSize, offsets[i]+kSize); + EXPECT_TRUE(allocator_->CheckConsistency()); + } + + // This allocation should fail. + FencedAllocator::Offset offset_failed = allocator_->Alloc(kSize); + EXPECT_EQ(FencedAllocator::kInvalidOffset, offset_failed); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // Free one successful allocation, pending fence. + int32 token = helper_.get()->InsertToken(); + allocator_->FreePendingToken(offsets[0], token); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // The way we hooked up the helper and engine, it won't process commands + // until it has to wait for something. Which means the token shouldn't have + // passed yet at this point. + EXPECT_GT(token, command_buffer_->GetToken()); + + // This allocation will need to reclaim the space freed above, so that should + // process the commands until the token is passed. + offsets[0] = allocator_->Alloc(kSize); + EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[0]); + EXPECT_GE(kBufferSize, offsets[0]+kSize); + EXPECT_TRUE(allocator_->CheckConsistency()); + // Check that the token has indeed passed. + EXPECT_LE(token, command_buffer_->GetToken()); + + // Free up everything. + for (unsigned int i = 0; i < kAllocCount; ++i) { + allocator_->Free(offsets[i]); + EXPECT_TRUE(allocator_->CheckConsistency()); + } +} + +// Tests GetLargestFreeSize +TEST_F(FencedAllocatorTest, TestGetLargestFreeSize) { + EXPECT_TRUE(allocator_->CheckConsistency()); + EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSize()); + + FencedAllocator::Offset offset = allocator_->Alloc(kBufferSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset); + EXPECT_EQ(0u, allocator_->GetLargestFreeSize()); + allocator_->Free(offset); + EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSize()); + + const unsigned int kSize = 16; + offset = allocator_->Alloc(kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset); + // The following checks that the buffer is allocated "smartly" - which is + // dependent on the implementation. But both first-fit or best-fit would + // ensure that. + EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeSize()); + + // Allocate 2 more buffers (now 3), and then free the first two. This is to + // ensure a hole. Note that this is dependent on the first-fit current + // implementation. + FencedAllocator::Offset offset1 = allocator_->Alloc(kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset1); + FencedAllocator::Offset offset2 = allocator_->Alloc(kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset2); + allocator_->Free(offset); + allocator_->Free(offset1); + EXPECT_EQ(kBufferSize - 3 * kSize, allocator_->GetLargestFreeSize()); + + offset = allocator_->Alloc(kBufferSize - 3 * kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset); + EXPECT_EQ(2 * kSize, allocator_->GetLargestFreeSize()); + + offset1 = allocator_->Alloc(2 * kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset1); + EXPECT_EQ(0u, allocator_->GetLargestFreeSize()); + + allocator_->Free(offset); + allocator_->Free(offset1); + allocator_->Free(offset2); +} + +// Tests GetLargestFreeOrPendingSize +TEST_F(FencedAllocatorTest, TestGetLargestFreeOrPendingSize) { + EXPECT_TRUE(allocator_->CheckConsistency()); + EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); + + FencedAllocator::Offset offset = allocator_->Alloc(kBufferSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset); + EXPECT_EQ(0u, allocator_->GetLargestFreeOrPendingSize()); + allocator_->Free(offset); + EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); + + const unsigned int kSize = 16; + offset = allocator_->Alloc(kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset); + // The following checks that the buffer is allocates "smartly" - which is + // dependent on the implementation. But both first-fit or best-fit would + // ensure that. + EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeOrPendingSize()); + + // Allocate 2 more buffers (now 3), and then free the first two. This is to + // ensure a hole. Note that this is dependent on the first-fit current + // implementation. + FencedAllocator::Offset offset1 = allocator_->Alloc(kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset1); + FencedAllocator::Offset offset2 = allocator_->Alloc(kSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset2); + allocator_->Free(offset); + allocator_->Free(offset1); + EXPECT_EQ(kBufferSize - 3 * kSize, + allocator_->GetLargestFreeOrPendingSize()); + + // Free the last one, pending a token. + int32 token = helper_.get()->InsertToken(); + allocator_->FreePendingToken(offset2, token); + + // Now all the buffers have been freed... + EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); + // .. but one is still waiting for the token. + EXPECT_EQ(kBufferSize - 3 * kSize, + allocator_->GetLargestFreeSize()); + + // The way we hooked up the helper and engine, it won't process commands + // until it has to wait for something. Which means the token shouldn't have + // passed yet at this point. + EXPECT_GT(token, command_buffer_->GetToken()); + // This allocation will need to reclaim the space freed above, so that should + // process the commands until the token is passed, but it will succeed. + offset = allocator_->Alloc(kBufferSize); + ASSERT_NE(FencedAllocator::kInvalidOffset, offset); + // Check that the token has indeed passed. + EXPECT_LE(token, command_buffer_->GetToken()); + allocator_->Free(offset); + + // Everything now has been freed... + EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); + // ... for real. + EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSize()); +} + +// Test fixture for FencedAllocatorWrapper test - Creates a +// FencedAllocatorWrapper, using a CommandBufferHelper with a mock +// AsyncAPIInterface for its interface (calling it directly, not through the +// RPC mechanism), making sure Noops are ignored and SetToken are properly +// forwarded to the engine. +class FencedAllocatorWrapperTest : public BaseFencedAllocatorTest { + protected: + virtual void SetUp() { + BaseFencedAllocatorTest::SetUp(); + + // Though allocating this buffer isn't strictly necessary, it makes + // allocations point to valid addresses, so they could be used for + // something. + buffer_.reset(new char[kBufferSize]); + allocator_.reset(new FencedAllocatorWrapper(kBufferSize, helper_.get(), + buffer_.get())); + } + + virtual void TearDown() { + // If the GPUProcessor posts any tasks, this forces them to run. + MessageLoop::current()->RunAllPending(); + + EXPECT_TRUE(allocator_->CheckConsistency()); + allocator_.release(); + buffer_.release(); + + BaseFencedAllocatorTest::TearDown(); + } + + scoped_ptr<FencedAllocatorWrapper> allocator_; + scoped_array<char> buffer_; +}; + +// Checks basic alloc and free. +TEST_F(FencedAllocatorWrapperTest, TestBasic) { + allocator_->CheckConsistency(); + + const unsigned int kSize = 16; + void *pointer = allocator_->Alloc(kSize); + ASSERT_TRUE(pointer); + EXPECT_LE(buffer_.get(), static_cast<char *>(pointer)); + EXPECT_GE(kBufferSize, static_cast<char *>(pointer) - buffer_.get() + kSize); + EXPECT_TRUE(allocator_->CheckConsistency()); + + allocator_->Free(pointer); + EXPECT_TRUE(allocator_->CheckConsistency()); + + char *pointer_char = allocator_->AllocTyped<char>(kSize); + ASSERT_TRUE(pointer_char); + EXPECT_LE(buffer_.get(), pointer_char); + EXPECT_GE(buffer_.get() + kBufferSize, pointer_char + kSize); + allocator_->Free(pointer_char); + EXPECT_TRUE(allocator_->CheckConsistency()); + + unsigned int *pointer_uint = allocator_->AllocTyped<unsigned int>(kSize); + ASSERT_TRUE(pointer_uint); + EXPECT_LE(buffer_.get(), reinterpret_cast<char *>(pointer_uint)); + EXPECT_GE(buffer_.get() + kBufferSize, + reinterpret_cast<char *>(pointer_uint + kSize)); + + // Check that it did allocate kSize * sizeof(unsigned int). We can't tell + // directly, except from the remaining size. + EXPECT_EQ(kBufferSize - kSize * sizeof(*pointer_uint), + allocator_->GetLargestFreeSize()); + allocator_->Free(pointer_uint); +} + +// Checks out-of-memory condition. +TEST_F(FencedAllocatorWrapperTest, TestOutOfMemory) { + allocator_->CheckConsistency(); + + const unsigned int kSize = 16; + const unsigned int kAllocCount = kBufferSize / kSize; + CHECK(kAllocCount * kSize == kBufferSize); + + // Allocate several buffers to fill in the memory. + void *pointers[kAllocCount]; + for (unsigned int i = 0; i < kAllocCount; ++i) { + pointers[i] = allocator_->Alloc(kSize); + EXPECT_TRUE(pointers[i]); + EXPECT_TRUE(allocator_->CheckConsistency()); + } + + // This allocation should fail. + void *pointer_failed = allocator_->Alloc(kSize); + EXPECT_FALSE(pointer_failed); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // Free one successful allocation, reallocate with half the size + allocator_->Free(pointers[0]); + EXPECT_TRUE(allocator_->CheckConsistency()); + pointers[0] = allocator_->Alloc(kSize/2); + EXPECT_TRUE(pointers[0]); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // This allocation should fail as well. + pointer_failed = allocator_->Alloc(kSize); + EXPECT_FALSE(pointer_failed); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // Free up everything. + for (unsigned int i = 0; i < kAllocCount; ++i) { + allocator_->Free(pointers[i]); + EXPECT_TRUE(allocator_->CheckConsistency()); + } +} + +// Checks the free-pending-token mechanism. +TEST_F(FencedAllocatorWrapperTest, TestFreePendingToken) { + allocator_->CheckConsistency(); + + const unsigned int kSize = 16; + const unsigned int kAllocCount = kBufferSize / kSize; + CHECK(kAllocCount * kSize == kBufferSize); + + // Allocate several buffers to fill in the memory. + void *pointers[kAllocCount]; + for (unsigned int i = 0; i < kAllocCount; ++i) { + pointers[i] = allocator_->Alloc(kSize); + EXPECT_TRUE(pointers[i]); + EXPECT_TRUE(allocator_->CheckConsistency()); + } + + // This allocation should fail. + void *pointer_failed = allocator_->Alloc(kSize); + EXPECT_FALSE(pointer_failed); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // Free one successful allocation, pending fence. + int32 token = helper_.get()->InsertToken(); + allocator_->FreePendingToken(pointers[0], token); + EXPECT_TRUE(allocator_->CheckConsistency()); + + // The way we hooked up the helper and engine, it won't process commands + // until it has to wait for something. Which means the token shouldn't have + // passed yet at this point. + EXPECT_GT(token, command_buffer_->GetToken()); + + // This allocation will need to reclaim the space freed above, so that should + // process the commands until the token is passed. + pointers[0] = allocator_->Alloc(kSize); + EXPECT_TRUE(pointers[0]); + EXPECT_TRUE(allocator_->CheckConsistency()); + // Check that the token has indeed passed. + EXPECT_LE(token, command_buffer_->GetToken()); + + // Free up everything. + for (unsigned int i = 0; i < kAllocCount; ++i) { + allocator_->Free(pointers[i]); + EXPECT_TRUE(allocator_->CheckConsistency()); + } +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/client/id_allocator.cc b/o3d/gpu/command_buffer/client/id_allocator.cc new file mode 100644 index 0000000..49104e5 --- /dev/null +++ b/o3d/gpu/command_buffer/client/id_allocator.cc @@ -0,0 +1,85 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of IdAllocator. + +#include "gpu/command_buffer/client/id_allocator.h" + +namespace command_buffer { + +IdAllocator::IdAllocator() : bitmap_(1) { bitmap_[0] = 0; } + +static const unsigned int kBitsPerUint32 = sizeof(Uint32) * 8; // NOLINT + +// Looks for the first non-full entry, and return the first free bit in that +// entry. If all the entries are full, it will return the first bit of an entry +// that would be appended, but doesn't actually append that entry to the vector. +unsigned int IdAllocator::FindFirstFree() const { + size_t size = bitmap_.size(); + for (unsigned int i = 0; i < size; ++i) { + Uint32 value = bitmap_[i]; + if (value != 0xffffffffU) { + for (unsigned int j = 0; j < kBitsPerUint32; ++j) { + if (!(value & (1 << j))) return i * kBitsPerUint32 + j; + } + DLOG(FATAL) << "Code should not reach here."; + } + } + return size*kBitsPerUint32; +} + +// Sets the correct bit in the proper entry, resizing the vector if needed. +void IdAllocator::SetBit(unsigned int bit, bool value) { + size_t size = bitmap_.size(); + if (bit >= size * kBitsPerUint32) { + size_t newsize = bit / kBitsPerUint32 + 1; + bitmap_.resize(newsize); + for (size_t i = size; i < newsize; ++i) bitmap_[i] = 0; + } + Uint32 mask = 1U << (bit % kBitsPerUint32); + if (value) { + bitmap_[bit / kBitsPerUint32] |= mask; + } else { + bitmap_[bit / kBitsPerUint32] &= ~mask; + } +} + +// Gets the bit from the proper entry. This doesn't resize the vector, just +// returns false if the bit is beyond the last entry. +bool IdAllocator::GetBit(unsigned int bit) const { + size_t size = bitmap_.size(); + if (bit / kBitsPerUint32 >= size) return false; + Uint32 mask = 1U << (bit % kBitsPerUint32); + return (bitmap_[bit / kBitsPerUint32] & mask) != 0; +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/client/id_allocator.h b/o3d/gpu/command_buffer/client/id_allocator.h new file mode 100644 index 0000000..b2b14b9 --- /dev/null +++ b/o3d/gpu/command_buffer/client/id_allocator.h @@ -0,0 +1,78 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the IdAllocator class. + +#ifndef GPU_COMMAND_BUFFER_CLIENT_CROSS_ID_ALLOCATOR_H_ +#define GPU_COMMAND_BUFFER_CLIENT_CROSS_ID_ALLOCATOR_H_ + +#include <vector> +#include "base/basictypes.h" +#include "gpu/command_buffer/common/types.h" +#include "gpu/command_buffer/common/resource.h" + +namespace command_buffer { + +// A class to manage the allocation of resource IDs. It uses a bitfield stored +// into a vector of unsigned ints. +class IdAllocator { + public: + IdAllocator(); + + // Allocates a new resource ID. + command_buffer::ResourceId AllocateID() { + unsigned int bit = FindFirstFree(); + SetBit(bit, true); + return bit; + } + + // Frees a resource ID. + void FreeID(command_buffer::ResourceId id) { + SetBit(id, false); + } + + // Checks whether or not a resource ID is in use. + bool InUse(command_buffer::ResourceId id) { + return GetBit(id); + } + private: + void SetBit(unsigned int bit, bool value); + bool GetBit(unsigned int bit) const; + unsigned int FindFirstFree() const; + + std::vector<Uint32> bitmap_; + DISALLOW_COPY_AND_ASSIGN(IdAllocator); +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_CLIENT_CROSS_ID_ALLOCATOR_H_ diff --git a/o3d/gpu/command_buffer/client/id_allocator_test.cc b/o3d/gpu/command_buffer/client/id_allocator_test.cc new file mode 100644 index 0000000..10c7809 --- /dev/null +++ b/o3d/gpu/command_buffer/client/id_allocator_test.cc @@ -0,0 +1,112 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file has the unit tests for the IdAllocator class. + +#include "tests/common/win/testing_common.h" +#include "gpu/command_buffer/client/id_allocator.h" + +namespace command_buffer { + +using command_buffer::ResourceId; + +class IdAllocatorTest : public testing::Test { + protected: + virtual void SetUp() {} + virtual void TearDown() {} + + IdAllocator* id_allocator() { return &id_allocator_; } + + private: + IdAllocator id_allocator_; +}; + +// Checks basic functionality: AllocateID, FreeID, InUse. +TEST_F(IdAllocatorTest, TestBasic) { + IdAllocator *allocator = id_allocator(); + // Check that resource 0 is not in use + EXPECT_FALSE(allocator->InUse(0)); + + // Allocate an ID, check that it's in use. + ResourceId id1 = allocator->AllocateID(); + EXPECT_TRUE(allocator->InUse(id1)); + + // Allocate another ID, check that it's in use, and different from the first + // one. + ResourceId id2 = allocator->AllocateID(); + EXPECT_TRUE(allocator->InUse(id2)); + EXPECT_NE(id1, id2); + + // Free one of the IDs, check that it's not in use any more. + allocator->FreeID(id1); + EXPECT_FALSE(allocator->InUse(id1)); + + // Frees the other ID, check that it's not in use any more. + allocator->FreeID(id2); + EXPECT_FALSE(allocator->InUse(id2)); +} + +// Checks that the resource IDs are allocated conservatively, and re-used after +// being freed. +TEST_F(IdAllocatorTest, TestAdvanced) { + IdAllocator *allocator = id_allocator(); + + // Allocate a significant number of resources. + const unsigned int kNumResources = 100; + ResourceId ids[kNumResources]; + for (unsigned int i = 0; i < kNumResources; ++i) { + ids[i] = allocator->AllocateID(); + EXPECT_TRUE(allocator->InUse(ids[i])); + } + + // Check that the allocation is conservative with resource IDs, that is that + // the resource IDs don't go over kNumResources - so that the service doesn't + // have to allocate too many internal structures when the resources are used. + for (unsigned int i = 0; i < kNumResources; ++i) { + EXPECT_GT(kNumResources, ids[i]); + } + + // Check that the next resources are still free. + for (unsigned int i = 0; i < kNumResources; ++i) { + EXPECT_FALSE(allocator->InUse(kNumResources + i)); + } + + // Check that a new allocation re-uses the resource we just freed. + ResourceId id1 = ids[kNumResources / 2]; + allocator->FreeID(id1); + EXPECT_FALSE(allocator->InUse(id1)); + ResourceId id2 = allocator->AllocateID(); + EXPECT_TRUE(allocator->InUse(id2)); + EXPECT_EQ(id1, id2); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/client/o3d_cmd_helper.cc b/o3d/gpu/command_buffer/client/o3d_cmd_helper.cc new file mode 100644 index 0000000..85c6a27 --- /dev/null +++ b/o3d/gpu/command_buffer/client/o3d_cmd_helper.cc @@ -0,0 +1,42 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the o3d buffer helper class. + +#include "gpu/command_buffer/client/o3d_cmd_helper.h" + +namespace command_buffer { + +// Currently this is a place holder. + +} // namespace command_buffer + diff --git a/o3d/gpu/command_buffer/client/o3d_cmd_helper.h b/o3d/gpu/command_buffer/client/o3d_cmd_helper.h new file mode 100644 index 0000000..0dbbff4 --- /dev/null +++ b/o3d/gpu/command_buffer/client/o3d_cmd_helper.h @@ -0,0 +1,636 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the o3d command buffer helper class. + +#ifndef GPU_COMMAND_BUFFER_CLIENT_CROSS_O3D_CMD_HELPER_H_ +#define GPU_COMMAND_BUFFER_CLIENT_CROSS_O3D_CMD_HELPER_H_ + +#include "gpu/command_buffer/common/logging.h" +#include "gpu/command_buffer/common/constants.h" +#include "gpu/command_buffer/client/cmd_buffer_helper.h" +#include "gpu/command_buffer/common/o3d_cmd_format.h" +#include "gpu/np_utils/np_object_pointer.h" + +namespace command_buffer { + +// A helper for O3D command buffers. +class O3DCmdHelper : public CommandBufferHelper { + public: + O3DCmdHelper( + NPP npp, + const gpu_plugin::NPObjectPointer<gpu_plugin::CommandBuffer>& + command_buffer) + : CommandBufferHelper(npp, command_buffer) { + } + virtual ~O3DCmdHelper() { + } + + // ------------------ Individual commands ---------------------- + + void BeginFrame() { + o3d::BeginFrame& cmd = GetCmdSpace<o3d::BeginFrame>(); + cmd.Init(); + } + + + void EndFrame() { + o3d::EndFrame& cmd = GetCmdSpace<o3d::EndFrame>(); + cmd.Init(); + } + + void Clear( + uint32 buffers, + float red, float green, float blue, float alpha, + float depth, uint32 stencil) { + o3d::Clear& cmd = GetCmdSpace<o3d::Clear>(); + cmd.Init(buffers, red, green, blue, alpha, depth, stencil); + } + + void SetViewport( + uint32 left, + uint32 top, + uint32 width, + uint32 height, + float z_min, + float z_max) { + o3d::SetViewport& cmd = GetCmdSpace<o3d::SetViewport>(); + cmd.Init(left, top, width, height, z_min, z_max); + } + + void CreateVertexBuffer( + ResourceId vertex_buffer_id, uint32 size, vertex_buffer::Flags flags) { + o3d::CreateVertexBuffer& cmd = GetCmdSpace<o3d::CreateVertexBuffer>(); + cmd.Init(vertex_buffer_id, size, flags); + } + + void DestroyVertexBuffer(ResourceId vertex_buffer_id) { + o3d::DestroyVertexBuffer& cmd = GetCmdSpace<o3d::DestroyVertexBuffer>(); + cmd.Init(vertex_buffer_id); + } + + void SetVertexBufferDataImmediate( + ResourceId vertex_buffer_id, uint32 offset, + const void* data, uint32 size) { + o3d::SetVertexBufferDataImmediate& cmd = + GetImmediateCmdSpace<o3d::SetVertexBufferDataImmediate>(size); + cmd.Init(vertex_buffer_id, offset, data, size); + } + + void SetVertexBufferData( + ResourceId vertex_buffer_id, uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::SetVertexBufferData& cmd = + GetCmdSpace<o3d::SetVertexBufferData>(); + cmd.Init(vertex_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + } + + void GetVertexBufferData( + ResourceId vertex_buffer_id, uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::GetVertexBufferData& cmd = + GetCmdSpace<o3d::GetVertexBufferData>(); + cmd.Init(vertex_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + } + + void CreateIndexBuffer( + ResourceId index_buffer_id, uint32 size, index_buffer::Flags flags) { + o3d::CreateIndexBuffer& cmd = + GetCmdSpace<o3d::CreateIndexBuffer>(); + cmd.Init(index_buffer_id, size, flags); + } + + void DestroyIndexBuffer(ResourceId index_buffer_id) { + o3d::DestroyIndexBuffer& cmd = GetCmdSpace<o3d::DestroyIndexBuffer>(); + cmd.Init(index_buffer_id); + } + + void SetIndexBufferDataImmediate( + ResourceId index_buffer_id, uint32 offset, + const void* data, uint32 size) { + o3d::SetIndexBufferDataImmediate& cmd = + GetImmediateCmdSpace<o3d::SetIndexBufferDataImmediate>(size); + cmd.Init(index_buffer_id, offset, data, size); + } + + void SetIndexBufferData( + ResourceId index_buffer_id, uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::SetIndexBufferData& cmd = GetCmdSpace<o3d::SetIndexBufferData>(); + cmd.Init(index_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + } + + void GetIndexBufferData( + ResourceId index_buffer_id, uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::GetIndexBufferData& cmd = GetCmdSpace<o3d::GetIndexBufferData>(); + cmd.Init(index_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + } + + void CreateVertexStruct(ResourceId vertex_struct_id, uint32 input_count) { + o3d::CreateVertexStruct& cmd = GetCmdSpace<o3d::CreateVertexStruct>(); + cmd.Init(vertex_struct_id, input_count); + } + + void DestroyVertexStruct(ResourceId vertex_struct_id) { + o3d::DestroyVertexStruct& cmd = GetCmdSpace<o3d::DestroyVertexStruct>(); + cmd.Init(vertex_struct_id); + } + + void SetVertexInput( + ResourceId vertex_struct_id, + uint32 input_index, + ResourceId vertex_buffer_id, + uint32 offset, + vertex_struct::Semantic semantic, + uint32 semantic_index, + vertex_struct::Type type, + uint32 stride) { + o3d::SetVertexInput& cmd = GetCmdSpace<o3d::SetVertexInput>(); + cmd.Init( + vertex_struct_id, + input_index, + vertex_buffer_id, + offset, + semantic, + semantic_index, + type, + stride); + } + + void SetVertexStruct(ResourceId vertex_struct_id) { + o3d::SetVertexStruct& cmd = GetCmdSpace<o3d::SetVertexStruct>(); + cmd.Init(vertex_struct_id); + } + + void Draw(o3d::PrimitiveType primitive_type, uint32 first, uint32 count) { + o3d::Draw& cmd = GetCmdSpace<o3d::Draw>(); + cmd.Init(primitive_type, first, count); + } + + void DrawIndexed( + o3d::PrimitiveType primitive_type, + ResourceId index_buffer_id, + uint32 first, + uint32 count, + uint32 min_index, + uint32 max_index) { + o3d::DrawIndexed& cmd = GetCmdSpace<o3d::DrawIndexed>(); + cmd.Init( + primitive_type, + index_buffer_id, + first, + count, + min_index, + max_index); + } + + void CreateEffect( + ResourceId effect_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::CreateEffect& cmd = GetCmdSpace<o3d::CreateEffect>(); + cmd.Init(effect_id, size, shared_memory_id, shared_memory_offset); + } + + void CreateEffectImmediate( + ResourceId effect_id, uint32 size, const void* data) { + o3d::CreateEffectImmediate& cmd = + GetImmediateCmdSpace<o3d::CreateEffectImmediate>(size); + cmd.Init(effect_id, size, data); + } + + void DestroyEffect(ResourceId effect_id) { + o3d::DestroyEffect& cmd = GetCmdSpace<o3d::DestroyEffect>(); + cmd.Init(effect_id); + } + + void SetEffect(ResourceId effect_id) { + o3d::SetEffect& cmd = GetCmdSpace<o3d::SetEffect>(); + cmd.Init(effect_id); + } + + void GetParamCount( + ResourceId effect_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::GetParamCount& cmd = GetCmdSpace<o3d::GetParamCount>(); + cmd.Init(effect_id, size, shared_memory_id, shared_memory_offset); + } + + void CreateParam(ResourceId param_id, ResourceId effect_id, uint32 index) { + o3d::CreateParam& cmd = GetCmdSpace<o3d::CreateParam>(); + cmd.Init(param_id, effect_id, index); + } + + void CreateParamByName( + ResourceId param_id, ResourceId effect_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::CreateParamByName& cmd = GetCmdSpace<o3d::CreateParamByName>(); + cmd.Init(param_id, effect_id, size, shared_memory_id, shared_memory_offset); + } + + void CreateParamByNameImmediate( + ResourceId param_id, ResourceId effect_id, + uint32 size, const void* data) { + o3d::CreateParamByNameImmediate& cmd = + GetImmediateCmdSpace<o3d::CreateParamByNameImmediate>(size); + cmd.Init(param_id, effect_id, size, data); + } + + void DestroyParam(ResourceId param_id) { + o3d::DestroyParam& cmd = GetCmdSpace<o3d::DestroyParam>(); + cmd.Init(param_id); + } + + void SetParamData( + ResourceId param_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::SetParamData& cmd = GetCmdSpace<o3d::SetParamData>(); + cmd.Init(param_id, size, shared_memory_id, shared_memory_offset); + } + + void SetParamDataImmediate( + ResourceId param_id, uint32 size, const void* data) { + o3d::SetParamDataImmediate& cmd = + GetImmediateCmdSpace<o3d::SetParamDataImmediate>(size); + cmd.Init(param_id, size, data); + } + + void GetParamDesc( + ResourceId param_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::GetParamDesc& cmd = GetCmdSpace<o3d::GetParamDesc>(); + cmd.Init(param_id, size, shared_memory_id, shared_memory_offset); + } + + void GetStreamCount( + ResourceId effect_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::GetStreamCount& cmd = GetCmdSpace<o3d::GetStreamCount>(); + cmd.Init(effect_id, size, shared_memory_id, shared_memory_offset); + } + + void GetStreamDesc( + ResourceId effect_id, uint32 index, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + o3d::GetStreamDesc& cmd = GetCmdSpace<o3d::GetStreamDesc>(); + cmd.Init(effect_id, index, size, shared_memory_id, shared_memory_offset); + } + + void DestroyTexture(ResourceId texture_id) { + o3d::DestroyTexture& cmd = GetCmdSpace<o3d::DestroyTexture>(); + cmd.Init(texture_id); + } + + void CreateTexture2d( + ResourceId texture_id, + uint32 width, uint32 height, + uint32 levels, texture::Format format, + bool enable_render_surfaces) { + o3d::CreateTexture2d& cmd = GetCmdSpace<o3d::CreateTexture2d>(); + cmd.Init(texture_id, + width, height, levels, format, + enable_render_surfaces); + } + + void CreateTexture3d( + ResourceId texture_id, + uint32 width, uint32 height, uint32 depth, + uint32 levels, texture::Format format, + bool enable_render_surfaces) { + o3d::CreateTexture3d& cmd = GetCmdSpace<o3d::CreateTexture3d>(); + cmd.Init(texture_id, + width, height, depth, + levels, format, + enable_render_surfaces); + } + + void CreateTextureCube( + ResourceId texture_id, + uint32 edge_length, uint32 levels, texture::Format format, + bool enable_render_surfaces) { + o3d::CreateTextureCube& cmd = GetCmdSpace<o3d::CreateTextureCube>(); + cmd.Init(texture_id, + edge_length, levels, format, + enable_render_surfaces); + } + + void SetTextureData( + ResourceId texture_id, + uint32 x, + uint32 y, + uint32 z, + uint32 width, + uint32 height, + uint32 depth, + uint32 level, + texture::Face face, + uint32 row_pitch, + uint32 slice_pitch, + uint32 size, + uint32 shared_memory_id, + uint32 shared_memory_offset) { + o3d::SetTextureData& cmd = GetCmdSpace<o3d::SetTextureData>(); + cmd.Init( + texture_id, + x, + y, + z, + width, + height, + depth, + level, + face, + row_pitch, + slice_pitch, + size, + shared_memory_id, + shared_memory_offset); + } + + void SetTextureDataImmediate( + ResourceId texture_id, + uint32 x, + uint32 y, + uint32 z, + uint32 width, + uint32 height, + uint32 depth, + uint32 level, + texture::Face face, + uint32 row_pitch, + uint32 slice_pitch, + uint32 size, + const void* data) { + o3d::SetTextureDataImmediate& cmd = + GetImmediateCmdSpace<o3d::SetTextureDataImmediate>(size); + cmd.Init( + texture_id, + x, + y, + z, + width, + height, + depth, + level, + face, + row_pitch, + slice_pitch, + size, + data); + } + + void GetTextureData( + ResourceId texture_id, + uint32 x, + uint32 y, + uint32 z, + uint32 width, + uint32 height, + uint32 depth, + uint32 level, + texture::Face face, + uint32 row_pitch, + uint32 slice_pitch, + uint32 size, + uint32 shared_memory_id, + uint32 shared_memory_offset) { + o3d::GetTextureData& cmd = GetCmdSpace<o3d::GetTextureData>(); + cmd.Init( + texture_id, + x, + y, + z, + width, + height, + depth, + level, + face, + row_pitch, + slice_pitch, + size, + shared_memory_id, + shared_memory_offset); + } + + void CreateSampler(ResourceId sampler_id) { + o3d::CreateSampler& cmd = GetCmdSpace<o3d::CreateSampler>(); + cmd.Init(sampler_id); + } + + void DestroySampler(ResourceId sampler_id) { + o3d::DestroySampler& cmd = GetCmdSpace<o3d::DestroySampler>(); + cmd.Init(sampler_id); + } + + void SetSamplerStates( + ResourceId sampler_id, + sampler::AddressingMode address_u_value, + sampler::AddressingMode address_v_value, + sampler::AddressingMode address_w_value, + sampler::FilteringMode mag_filter_value, + sampler::FilteringMode min_filter_value, + sampler::FilteringMode mip_filter_value, + uint8 max_anisotropy) { + o3d::SetSamplerStates& cmd = GetCmdSpace<o3d::SetSamplerStates>(); + cmd.Init( + sampler_id, + address_u_value, + address_v_value, + address_w_value, + mag_filter_value, + min_filter_value, + mip_filter_value, + max_anisotropy); + } + + void SetSamplerBorderColor( + ResourceId sampler_id, + float red, float green, float blue, float alpha) { + o3d::SetSamplerBorderColor& cmd = + GetCmdSpace<o3d::SetSamplerBorderColor>(); + cmd.Init(sampler_id, red, green, blue, alpha); + } + + void SetSamplerTexture(ResourceId sampler_id, ResourceId texture_id) { + o3d::SetSamplerTexture& cmd = GetCmdSpace<o3d::SetSamplerTexture>(); + cmd.Init(sampler_id, texture_id); + } + + void SetScissor( + uint32 x, + uint32 y, + uint32 width, + uint32 height, + bool enable) { + o3d::SetScissor& cmd = GetCmdSpace<o3d::SetScissor>(); + cmd.Init( + x, + y, + width, + height, + enable); + } + + void SetPolygonOffset(float slope_factor, float units) { + o3d::SetPolygonOffset& cmd = GetCmdSpace<o3d::SetPolygonOffset>(); + cmd.Init(slope_factor, units); + } + + void SetPointLineRaster( + bool line_smooth_enable, bool point_sprite_enable, float point_size) { + o3d::SetPointLineRaster& cmd = GetCmdSpace<o3d::SetPointLineRaster>(); + cmd.Init(line_smooth_enable, point_sprite_enable, point_size); + } + + void SetPolygonRaster(o3d::PolygonMode fill_mode, + o3d::FaceCullMode cull_mode) { + o3d::SetPolygonRaster& cmd = GetCmdSpace<o3d::SetPolygonRaster>(); + cmd.Init(fill_mode, cull_mode); + } + + void SetAlphaTest(o3d::Comparison func, bool enable, float value) { + o3d::SetAlphaTest& cmd = GetCmdSpace<o3d::SetAlphaTest>(); + cmd.Init(func, enable, value); + } + + void SetDepthTest(o3d::Comparison func, bool write_enable, bool enable) { + o3d::SetDepthTest& cmd = GetCmdSpace<o3d::SetDepthTest>(); + cmd.Init(func, write_enable, enable); + } + + void SetStencilTest( + uint8 write_mask, + uint8 compare_mask, + uint8 reference_value, + bool separate_ccw, + bool enable, + o3d::Comparison cw_func, + o3d::StencilOp cw_pass_op, + o3d::StencilOp cw_fail_op, + o3d::StencilOp cw_z_fail_op, + o3d::Comparison ccw_func, + o3d::StencilOp ccw_pass_op, + o3d::StencilOp ccw_fail_op, + o3d::StencilOp ccw_z_fail_op) { + o3d::SetStencilTest& cmd = GetCmdSpace<o3d::SetStencilTest>(); + cmd.Init( + write_mask, + compare_mask, + reference_value, + separate_ccw, + enable, + cw_func, + cw_pass_op, + cw_fail_op, + cw_z_fail_op, + ccw_func, + ccw_pass_op, + ccw_fail_op, + ccw_z_fail_op); + } + + void SetColorWrite(uint8 mask, bool dither_enable) { + o3d::SetColorWrite& cmd = GetCmdSpace<o3d::SetColorWrite>(); + cmd.Init(mask, dither_enable); + } + + void SetBlending( + o3d::BlendFunc color_src_func, + o3d::BlendFunc color_dst_func, + o3d::BlendEq color_eq, + o3d::BlendFunc alpha_src_func, + o3d::BlendFunc alpha_dst_func, + o3d::BlendEq alpha_eq, + bool separate_alpha, + bool enable) { + o3d::SetBlending& cmd = GetCmdSpace<o3d::SetBlending>(); + cmd.Init( + color_src_func, + color_dst_func, + color_eq, + alpha_src_func, + alpha_dst_func, + alpha_eq, + separate_alpha, + enable); + } + + void SetBlendingColor(float red, float green, float blue, float alpha) { + o3d::SetBlendingColor& cmd = GetCmdSpace<o3d::SetBlendingColor>(); + cmd.Init(red, green, blue, alpha); + } + + void CreateRenderSurface( + ResourceId render_surface_id, ResourceId texture_id, + uint32 width, uint32 height, + uint32 level, uint32 side) { + o3d::CreateRenderSurface& cmd = GetCmdSpace<o3d::CreateRenderSurface>(); + cmd.Init(render_surface_id, texture_id, width, height, level, side); + } + + void DestroyRenderSurface(ResourceId render_surface_id) { + o3d::DestroyRenderSurface& cmd = + GetCmdSpace<o3d::DestroyRenderSurface>(); + cmd.Init(render_surface_id); + } + + void CreateDepthSurface( + ResourceId depth_surface_id, uint32 width, uint32 height) { + o3d::CreateDepthSurface& cmd = GetCmdSpace<o3d::CreateDepthSurface>(); + cmd.Init(depth_surface_id, width, height); + } + + void DestroyDepthSurface(ResourceId depth_surface_id) { + o3d::DestroyDepthSurface& cmd = GetCmdSpace<o3d::DestroyDepthSurface>(); + cmd.Init(depth_surface_id); + } + + void SetRenderSurface( + ResourceId render_surface_id, ResourceId depth_surface_id) { + o3d::SetRenderSurface& cmd = GetCmdSpace<o3d::SetRenderSurface>(); + cmd.Init(render_surface_id, depth_surface_id); + } + + void SetBackSurfaces() { + o3d::SetBackSurfaces& cmd = GetCmdSpace<o3d::SetBackSurfaces>(); + cmd.Init(); + } +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_CLIENT_CROSS_O3D_CMD_HELPER_H_ + diff --git a/o3d/gpu/command_buffer/common/GLES2/gl2.h b/o3d/gpu/command_buffer/common/GLES2/gl2.h new file mode 100644 index 0000000..94c643b --- /dev/null +++ b/o3d/gpu/command_buffer/common/GLES2/gl2.h @@ -0,0 +1,621 @@ +#ifndef __gl2_h_ +#define __gl2_h_ + +/* $Revision: 8784 $ on $Date:: 2009-09-02 09:49:17 -0700 #$ */ + +#include <GLES2/gl2platform.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/*------------------------------------------------------------------------- + * Data type definitions + *-----------------------------------------------------------------------*/ + +typedef void GLvoid; +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef khronos_int8_t GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef khronos_uint8_t GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef khronos_int32_t GLfixed; + +/* GL types for handling large vertex buffer objects */ +typedef khronos_intptr_t GLintptr; +typedef khronos_ssize_t GLsizeiptr; + +/* OpenGL ES core versions */ +#define GL_ES_VERSION_2_0 1 + +/* ClearBufferMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 + +/* Boolean */ +#define GL_FALSE 0 +#define GL_TRUE 1 + +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +/* AlphaFunction (not supported in ES20) */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 + +/* BlendingFactorSrc */ +/* GL_ZERO */ +/* GL_ONE */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* GL_SRC_ALPHA */ +/* GL_ONE_MINUS_SRC_ALPHA */ +/* GL_DST_ALPHA */ +/* GL_ONE_MINUS_DST_ALPHA */ + +/* BlendEquationSeparate */ +#define GL_FUNC_ADD 0x8006 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_RGB 0x8009 /* same as BLEND_EQUATION */ +#define GL_BLEND_EQUATION_ALPHA 0x883D + +/* BlendSubtract */ +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B + +/* Separate Blend Functions */ +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_BLEND_COLOR 0x8005 + +/* Buffer Objects */ +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 + +#define GL_STREAM_DRAW 0x88E0 +#define GL_STATIC_DRAW 0x88E4 +#define GL_DYNAMIC_DRAW 0x88E8 + +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 + +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 + +/* CullFaceMode */ +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_FRONT_AND_BACK 0x0408 + +/* DepthFunction */ +/* GL_NEVER */ +/* GL_LESS */ +/* GL_EQUAL */ +/* GL_LEQUAL */ +/* GL_GREATER */ +/* GL_NOTEQUAL */ +/* GL_GEQUAL */ +/* GL_ALWAYS */ + +/* EnableCap */ +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CULL_FACE 0x0B44 +#define GL_BLEND 0x0BE2 +#define GL_DITHER 0x0BD0 +#define GL_STENCIL_TEST 0x0B90 +#define GL_DEPTH_TEST 0x0B71 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_COVERAGE 0x80A0 + +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 + +/* FrontFaceDirection */ +#define GL_CW 0x0900 +#define GL_CCW 0x0901 + +/* GetPName */ +#define GL_LINE_WIDTH 0x0B21 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VIEWPORT 0x0BA2 +#define GL_SCISSOR_BOX 0x0C10 +/* GL_SCISSOR_TEST */ +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +/* GL_POLYGON_OFFSET_FILL */ +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB + +/* GetTextureParameter */ +/* GL_TEXTURE_MAG_FILTER */ +/* GL_TEXTURE_MIN_FILTER */ +/* GL_TEXTURE_WRAP_S */ +/* GL_TEXTURE_WRAP_T */ + +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 + +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* HintTarget */ +#define GL_GENERATE_MIPMAP_HINT 0x8192 + +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_FIXED 0x140C + +/* PixelFormat */ +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A + +/* PixelType */ +/* GL_UNSIGNED_BYTE */ +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 + +/* Shaders */ +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_SHADER_TYPE 0x8B4F +#define GL_DELETE_STATUS 0x8B80 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D + +/* StencilFunction */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +/* StencilOp */ +/* GL_ZERO */ +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_INVERT 0x150A +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 + +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +/* TextureMinFilter */ +/* GL_NEAREST */ +/* GL_LINEAR */ +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 + +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 + +/* TextureTarget */ +/* GL_TEXTURE_2D */ +#define GL_TEXTURE 0x1702 + +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C + +/* TextureUnit */ +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 + +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_MIRRORED_REPEAT 0x8370 + +/* Uniform Types */ +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_CUBE 0x8B60 + +/* Vertex Arrays */ +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F + +/* Read Format */ +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B + +/* Shader Source */ +#define GL_COMPILE_STATUS 0x8B81 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_COMPILER 0x8DFA + +/* Shader Binary */ +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 + +/* Shader Precision-Specified Types */ +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 + +/* Framebuffer Object. */ +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 + +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGB565 0x8D62 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_INDEX8 0x8D48 + +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 + +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 + +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 + +#define GL_NONE 0 + +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD + +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 + +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 + +/*------------------------------------------------------------------------- + * GL core functions. + *-----------------------------------------------------------------------*/ + +GL_APICALL void GL_APIENTRY glActiveTexture (GLenum texture); +GL_APICALL void GL_APIENTRY glAttachShader (GLuint program, GLuint shader); +GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const char* name); +GL_APICALL void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GL_APICALL void GL_APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GL_APICALL void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GL_APICALL void GL_APIENTRY glBindTexture (GLenum target, GLuint texture); +GL_APICALL void GL_APIENTRY glBlendColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GL_APICALL void GL_APIENTRY glBlendEquation ( GLenum mode ); +GL_APICALL void GL_APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GL_APICALL void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GL_APICALL void GL_APIENTRY glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void* data, GLenum usage); +GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void* data); +GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus (GLenum target); +GL_APICALL void GL_APIENTRY glClear (GLbitfield mask); +GL_APICALL void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GL_APICALL void GL_APIENTRY glClearDepthf (GLclampf depth); +GL_APICALL void GL_APIENTRY glClearStencil (GLint s); +GL_APICALL void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GL_APICALL void GL_APIENTRY glCompileShader (GLuint shader); +GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); +GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); +GL_APICALL void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GL_APICALL void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL GLuint GL_APIENTRY glCreateProgram (void); +GL_APICALL GLuint GL_APIENTRY glCreateShader (GLenum type); +GL_APICALL void GL_APIENTRY glCullFace (GLenum mode); +GL_APICALL void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint* buffers); +GL_APICALL void GL_APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint* framebuffers); +GL_APICALL void GL_APIENTRY glDeleteProgram (GLuint program); +GL_APICALL void GL_APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint* renderbuffers); +GL_APICALL void GL_APIENTRY glDeleteShader (GLuint shader); +GL_APICALL void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint* textures); +GL_APICALL void GL_APIENTRY glDepthFunc (GLenum func); +GL_APICALL void GL_APIENTRY glDepthMask (GLboolean flag); +GL_APICALL void GL_APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar); +GL_APICALL void GL_APIENTRY glDetachShader (GLuint program, GLuint shader); +GL_APICALL void GL_APIENTRY glDisable (GLenum cap); +GL_APICALL void GL_APIENTRY glDisableVertexAttribArray (GLuint index); +GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void* indices); +GL_APICALL void GL_APIENTRY glEnable (GLenum cap); +GL_APICALL void GL_APIENTRY glEnableVertexAttribArray (GLuint index); +GL_APICALL void GL_APIENTRY glFinish (void); +GL_APICALL void GL_APIENTRY glFlush (void); +GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GL_APICALL void GL_APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GL_APICALL void GL_APIENTRY glFrontFace (GLenum mode); +GL_APICALL void GL_APIENTRY glGenBuffers (GLsizei n, GLuint* buffers); +GL_APICALL void GL_APIENTRY glGenerateMipmap (GLenum target); +GL_APICALL void GL_APIENTRY glGenFramebuffers (GLsizei n, GLuint* framebuffers); +GL_APICALL void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers); +GL_APICALL void GL_APIENTRY glGenTextures (GLsizei n, GLuint* textures); +GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); +GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); +GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); +GL_APICALL int GL_APIENTRY glGetAttribLocation (GLuint program, const char* name); +GL_APICALL void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean* params); +GL_APICALL void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint* params); +GL_APICALL GLenum GL_APIENTRY glGetError (void); +GL_APICALL void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetIntegerv (GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); +GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); +GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); +GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, char* source); +GL_APICALL const GLubyte* GL_APIENTRY glGetString (GLenum name); +GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetUniformiv (GLuint program, GLint location, GLint* params); +GL_APICALL int GL_APIENTRY glGetUniformLocation (GLuint program, const char* name); +GL_APICALL void GL_APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat* params); +GL_APICALL void GL_APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint* params); +GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void** pointer); +GL_APICALL void GL_APIENTRY glHint (GLenum target, GLenum mode); +GL_APICALL GLboolean GL_APIENTRY glIsBuffer (GLuint buffer); +GL_APICALL GLboolean GL_APIENTRY glIsEnabled (GLenum cap); +GL_APICALL GLboolean GL_APIENTRY glIsFramebuffer (GLuint framebuffer); +GL_APICALL GLboolean GL_APIENTRY glIsProgram (GLuint program); +GL_APICALL GLboolean GL_APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GL_APICALL GLboolean GL_APIENTRY glIsShader (GLuint shader); +GL_APICALL GLboolean GL_APIENTRY glIsTexture (GLuint texture); +GL_APICALL void GL_APIENTRY glLineWidth (GLfloat width); +GL_APICALL void GL_APIENTRY glLinkProgram (GLuint program); +GL_APICALL void GL_APIENTRY glPixelStorei (GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); +GL_APICALL void GL_APIENTRY glReleaseShaderCompiler (void); +GL_APICALL void GL_APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GL_APICALL void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length); +GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const char** string, const GLint* length); +GL_APICALL void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GL_APICALL void GL_APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask); +GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GL_APICALL void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass); +GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels); +GL_APICALL void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GL_APICALL void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat* params); +GL_APICALL void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GL_APICALL void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint* params); +GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels); +GL_APICALL void GL_APIENTRY glUniform1f (GLint location, GLfloat x); +GL_APICALL void GL_APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform1i (GLint location, GLint x); +GL_APICALL void GL_APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniform2f (GLint location, GLfloat x, GLfloat y); +GL_APICALL void GL_APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform2i (GLint location, GLint x, GLint y); +GL_APICALL void GL_APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniform3f (GLint location, GLfloat x, GLfloat y, GLfloat z); +GL_APICALL void GL_APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform3i (GLint location, GLint x, GLint y, GLint z); +GL_APICALL void GL_APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniform4f (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GL_APICALL void GL_APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat* v); +GL_APICALL void GL_APIENTRY glUniform4i (GLint location, GLint x, GLint y, GLint z, GLint w); +GL_APICALL void GL_APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint* v); +GL_APICALL void GL_APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +GL_APICALL void GL_APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +GL_APICALL void GL_APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +GL_APICALL void GL_APIENTRY glUseProgram (GLuint program); +GL_APICALL void GL_APIENTRY glValidateProgram (GLuint program); +GL_APICALL void GL_APIENTRY glVertexAttrib1f (GLuint indx, GLfloat x); +GL_APICALL void GL_APIENTRY glVertexAttrib1fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttrib2f (GLuint indx, GLfloat x, GLfloat y); +GL_APICALL void GL_APIENTRY glVertexAttrib2fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttrib3f (GLuint indx, GLfloat x, GLfloat y, GLfloat z); +GL_APICALL void GL_APIENTRY glVertexAttrib3fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttrib4f (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GL_APICALL void GL_APIENTRY glVertexAttrib4fv (GLuint indx, const GLfloat* values); +GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); +GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); + +#ifdef __cplusplus +} +#endif + +#endif /* __gl2_h_ */ + diff --git a/o3d/gpu/command_buffer/common/GLES2/gl2platform.h b/o3d/gpu/command_buffer/common/GLES2/gl2platform.h new file mode 100644 index 0000000..3e9036c --- /dev/null +++ b/o3d/gpu/command_buffer/common/GLES2/gl2platform.h @@ -0,0 +1,29 @@ +#ifndef __gl2platform_h_ +#define __gl2platform_h_ + +/* $Revision: 7173 $ on $Date:: 2009-01-09 11:18:21 -0800 #$ */ + +/* + * This document is licensed under the SGI Free Software B License Version + * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ . + */ + +/* Platform-specific types and definitions for OpenGL ES 2.X gl2.h + * Last modified on 2008/12/19 + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "OpenGL-ES" component "Registry". + */ + +#include <KHR/khrplatform.h> + +#ifndef GL_APICALL +#define GL_APICALL KHRONOS_APICALL +#endif + +#define GL_APIENTRY KHRONOS_APIENTRY + +#endif /* __gl2platform_h_ */ diff --git a/o3d/gpu/command_buffer/common/KHR/khrplatform.h b/o3d/gpu/command_buffer/common/KHR/khrplatform.h new file mode 100644 index 0000000..8341f71b --- /dev/null +++ b/o3d/gpu/command_buffer/common/KHR/khrplatform.h @@ -0,0 +1,269 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2009 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * $Revision: 7820 $ on $Date: 2009-04-03 13:46:26 -0700 (Fri, 03 Apr 2009) $ + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by sending them to the public Khronos Bugzilla + * (http://khronos.org/bugzilla) by filing a bug against product + * "Khronos (general)" component "Registry". + * + * A predefined template which fills in some of the bug fields can be + * reached using http://tinyurl.com/khrplatform-h-bugreport, but you + * must create a Bugzilla login first. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include <KHR/khrplatform.h> + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(_WIN32) && !defined(__SCITECH_SNAP__) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using <stdint.h> + */ +#include <stdint.h> +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using <inttypes.h> + */ +#include <inttypes.h> +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include <stdint.h> +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ diff --git a/o3d/gpu/command_buffer/common/bitfield_helpers.h b/o3d/gpu/command_buffer/common/bitfield_helpers.h new file mode 100644 index 0000000..b74374d --- /dev/null +++ b/o3d/gpu/command_buffer/common/bitfield_helpers.h @@ -0,0 +1,68 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains a helper template class used to access bit fields in +// unsigned int_ts. + +#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_BITFIELD_HELPERS_H_ +#define GPU_COMMAND_BUFFER_COMMON_CROSS_BITFIELD_HELPERS_H_ + +namespace command_buffer { + +// Bitfield template class, used to access bit fields in unsigned int_ts. +template<int shift, int length> class BitField { + public: + static const unsigned int kShift = shift; + static const unsigned int kLength = length; + // the following is really (1<<length)-1 but also work for length == 32 + // without compiler warning. + static const unsigned int kMask = 1U + ((1U << (length-1)) - 1U) * 2U; + + // Gets the value contained in this field. + static unsigned int Get(unsigned int container) { + return (container >> kShift) & kMask; + } + + // Makes a value that can be or-ed into this field. + static unsigned int MakeValue(unsigned int value) { + return (value & kMask) << kShift; + } + + // Changes the value of this field. + static void Set(unsigned int *container, unsigned int field_value) { + *container = (*container & ~(kMask << kShift)) | MakeValue(field_value); + } +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_BITFIELD_HELPERS_H_ diff --git a/o3d/gpu/command_buffer/common/bitfield_helpers_test.cc b/o3d/gpu/command_buffer/common/bitfield_helpers_test.cc new file mode 100644 index 0000000..60014d2 --- /dev/null +++ b/o3d/gpu/command_buffer/common/bitfield_helpers_test.cc @@ -0,0 +1,66 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// Tests for the bitfield helper class. + +#include "gtest/gtest.h" +#include "gpu/command_buffer/common/bitfield_helpers.h" + +namespace command_buffer { + +// Tests that BitField<>::Get returns the right bits. +TEST(BitFieldTest, TestGet) { + unsigned int value = 0x12345678u; + EXPECT_EQ(0x8u, (BitField<0, 4>::Get(value))); + EXPECT_EQ(0x45u, (BitField<12, 8>::Get(value))); + EXPECT_EQ(0x12345678u, (BitField<0, 32>::Get(value))); +} + +// Tests that BitField<>::MakeValue generates the right bits. +TEST(BitFieldTest, TestMakeValue) { + EXPECT_EQ(0x00000003u, (BitField<0, 4>::MakeValue(0x3))); + EXPECT_EQ(0x00023000u, (BitField<12, 8>::MakeValue(0x123))); + EXPECT_EQ(0x87654321u, (BitField<0, 32>::MakeValue(0x87654321))); +} + +// Tests that BitField<>::Set modifies the right bits. +TEST(BitFieldTest, TestSet) { + unsigned int value = 0x12345678u; + BitField<0, 4>::Set(&value, 0x9); + EXPECT_EQ(0x12345679u, value); + BitField<12, 8>::Set(&value, 0x123); + EXPECT_EQ(0x12323679u, value); + BitField<0, 32>::Set(&value, 0x87654321); + EXPECT_EQ(0x87654321u, value); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/common/cmd_buffer_common.cc b/o3d/gpu/command_buffer/common/cmd_buffer_common.cc new file mode 100644 index 0000000..e9172eb --- /dev/null +++ b/o3d/gpu/command_buffer/common/cmd_buffer_common.cc @@ -0,0 +1,57 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the binary format definition of the command buffer and +// command buffer commands. + +#include "gpu/command_buffer/common/cmd_buffer_common.h" + +namespace command_buffer { +namespace cmd { + +const char* GetCommandName(CommandId command_id) { + static const char* const names[] = { + #define COMMON_COMMAND_BUFFER_CMD_OP(name) # name, + + COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP) + + #undef COMMON_COMMAND_BUFFER_CMD_OP + }; + + int id = static_cast<int>(command_id); + return (id >= 0 && id < kNumCommands) ? names[id] : "*unknown-command*"; +} + +} // namespace cmd +} // namespace command_buffer + + diff --git a/o3d/gpu/command_buffer/common/cmd_buffer_common.h b/o3d/gpu/command_buffer/common/cmd_buffer_common.h new file mode 100644 index 0000000..0e44f86 --- /dev/null +++ b/o3d/gpu/command_buffer/common/cmd_buffer_common.h @@ -0,0 +1,247 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the common parts of command buffer formats. + +#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_COMMON_H_ +#define GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_COMMON_H_ + +#include "base/basictypes.h" +#include "gpu/command_buffer/common/types.h" +#include "gpu/command_buffer/common/bitfield_helpers.h" +#include "gpu/command_buffer/common/logging.h" + +namespace command_buffer { + +namespace cmd { + enum ArgFlags { + kFixed = 0x0, + kAtLeastN = 0x1, + }; +} // namespace cmd + +// Computes the number of command buffer entries needed for a certain size. In +// other words it rounds up to a multiple of entries. +inline uint32 ComputeNumEntries(size_t size_in_bytes) { + return static_cast<uint32>( + (size_in_bytes + sizeof(uint32) - 1) / sizeof(uint32)); // NOLINT +} + +// Rounds up to a multiple of entries in bytes. +inline size_t RoundSizeToMultipleOfEntries(size_t size_in_bytes) { + return ComputeNumEntries(size_in_bytes) * sizeof(uint32); // NOLINT +} + +// Struct that defines the command header in the command buffer. +struct CommandHeader { + Uint32 size:8; + Uint32 command:24; + + void Init(uint32 _command, uint32 _size) { + DCHECK_LT(_size, 256u); + command = _command; + size = _size; + } + + // Sets the header based on the passed in command. Can not be used for + // variable sized commands like immediate commands or Noop. + template <typename T> + void SetCmd() { + COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed); + Init(T::kCmdId, ComputeNumEntries(sizeof(T))); // NOLINT + } + + // Sets the header by a size in bytes. + template <typename T> + void SetCmdBySize(uint32 size_in_bytes) { + COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); + Init(T::kCmdId, ComputeNumEntries(sizeof(T) + size_in_bytes)); // NOLINT + } +}; + +COMPILE_ASSERT(sizeof(CommandHeader) == 4, Sizeof_CommandHeader_is_not_4); + +// Union that defines possible command buffer entries. +union CommandBufferEntry { + CommandHeader value_header; + Uint32 value_uint32; + Int32 value_int32; + float value_float; +}; + +COMPILE_ASSERT(sizeof(CommandBufferEntry) == 4, + Sizeof_CommandBufferEntry_is_not_4); + + +// Make sure the compiler does not add extra padding to any of the command +// structures. +#pragma pack(push, 1) + +// Gets the address of memory just after a structure in a typesafe way. This is +// used for IMMEDIATE commands to get the address of the place to put the data. +// Immediate command put their data direclty in the command buffer. +// Parameters: +// cmd: Address of command. +template <typename T> +void* ImmediateDataAddress(T* cmd) { + COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); + return reinterpret_cast<char*>(cmd) + sizeof(*cmd); +} + +// Gets the address of the place to put the next command in a typesafe way. +// This can only be used for fixed sized commands. +template <typename T> +// Parameters: +// cmd: Address of command. +void* NextCmdAddress(void* cmd) { + COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed); + return reinterpret_cast<char*>(cmd) + sizeof(T); +} + +// Gets the address of the place to put the next command in a typesafe way. +// This can only be used for variable sized command like IMMEDIATE commands. +// Parameters: +// cmd: Address of command. +// size_of_data_in_bytes: Size of the data for the command. +template <typename T> +void* NextImmediateCmdAddress(void* cmd, uint32 size_of_data_in_bytes) { + COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); + return reinterpret_cast<char*>(cmd) + sizeof(T) + // NOLINT + RoundSizeToMultipleOfEntries(size_of_data_in_bytes); +} + +struct SharedMemory { + void Init(uint32 _id, uint32 _offset) { + id = _id; + offset = _offset; + } + + uint32 id; + uint32 offset; +}; + +COMPILE_ASSERT(offsetof(SharedMemory, id) == 0, + Offsetof_SharedMemory_id_not_0); +COMPILE_ASSERT(offsetof(SharedMemory, offset) == 4, + Offsetof_SharedMemory_offset_not_4); + + +namespace cmd { + +// This macro is used to safely and convienently expand the list of commnad +// buffer commands in to various lists and never have them get out of sync. To +// add a new command, add it this list, create the corresponding structure below +// and then add a function in gapi_decoder.cc called Handle_COMMAND_NAME where +// COMMAND_NAME is the name of your command structure. +// +// NOTE: THE ORDER OF THESE MUST NOT CHANGE (their id is derived by order) +#define COMMON_COMMAND_BUFFER_CMDS(OP) \ + OP(Noop) /* 0 */ \ + OP(SetToken) /* 1 */ \ + +// Common commands. +enum CommandId { + #define COMMON_COMMAND_BUFFER_CMD_OP(name) k ## name, + + COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP) + + #undef COMMON_COMMAND_BUFFER_CMD_OP + + kNumCommands, + kLastCommonId = 1023, // reserve 1024 spaces for common commands. +}; + +COMPILE_ASSERT(kNumCommands - 1 <= kLastCommonId, Too_many_common_commands); + +const char* GetCommandName(CommandId id); + +struct Noop { + typedef Noop ValueType; + static const CommandId kCmdId = kNoop; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + void SetHeader(uint32 skip_count) { + header.Init(kCmdId, skip_count + 1); + } + + void Init(uint32 skip_count) { + SetHeader(skip_count); + } + + static void* Set(void* cmd, uint32 skip_count) { + static_cast<ValueType*>(cmd)->Init(skip_count); + return NextImmediateCmdAddress<ValueType>( + cmd, skip_count * sizeof(CommandBufferEntry)); // NOLINT + } + + CommandHeader header; +}; + +COMPILE_ASSERT(sizeof(Noop) == 4, Sizeof_Noop_is_not_4); +COMPILE_ASSERT(offsetof(Noop, header) == 0, Offsetof_Noop_header_not_0); + +struct SetToken { + typedef SetToken ValueType; + static const CommandId kCmdId = kSetToken; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(uint32 _token) { + SetHeader(); + token = _token; + } + static void* Set(void* cmd, uint32 token) { + static_cast<ValueType*>(cmd)->Init(token); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + uint32 token; +}; + +COMPILE_ASSERT(sizeof(SetToken) == 8, Sizeof_SetToken_is_not_8); +COMPILE_ASSERT(offsetof(SetToken, header) == 0, + Offsetof_SetToken_header_not_0); +COMPILE_ASSERT(offsetof(SetToken, token) == 4, + Offsetof_SetToken_token_not_4); + +} // namespace cmd + +#pragma pack(pop) + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_COMMON_H_ + diff --git a/o3d/gpu/command_buffer/common/constants.h b/o3d/gpu/command_buffer/common/constants.h new file mode 100644 index 0000000..ee874cd --- /dev/null +++ b/o3d/gpu/command_buffer/common/constants.h @@ -0,0 +1,71 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef O3D_COMMAND_BUFFER_COMMON_CROSS_CONSTANTS_H_ +#define O3D_COMMAND_BUFFER_COMMON_CROSS_CONSTANTS_H_ + +#include "base/basictypes.h" + +namespace command_buffer { + +typedef int32 CommandBufferOffset; +const CommandBufferOffset kInvalidCommandBufferOffset = -1; + +// Status of the command buffer service. It does not process commands +// (meaning: get will not change) unless in kParsing state. +namespace parser_status { + enum ParserStatus { + kNotConnected, // The service is not connected - initial state. + kNoBuffer, // The service is connected but no buffer was set. + kParsing, // The service is connected, and parsing commands from the + // buffer. + kParseError, // Parsing stopped because a parse error was found. + }; +} + +namespace parse_error { + enum ParseError { + kParseNoError, + kParseInvalidSize, + kParseOutOfBounds, + kParseUnknownCommand, + kParseInvalidArguments, + }; +} + +// Invalid shared memory Id, returned by RegisterSharedMemory in case of +// failure. +const int32 kInvalidSharedMemoryId = -1; + +} // namespace command_buffer + +#endif // O3D_COMMAND_BUFFER_COMMON_CROSS_CONSTANTS_H_ diff --git a/o3d/gpu/command_buffer/common/gapi_interface.h b/o3d/gpu/command_buffer/common/gapi_interface.h new file mode 100644 index 0000000..a032f0e --- /dev/null +++ b/o3d/gpu/command_buffer/common/gapi_interface.h @@ -0,0 +1,833 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the interface class for the low-level graphics API +// (GAPI). + +#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_GAPI_INTERFACE_H_ +#define GPU_COMMAND_BUFFER_COMMON_CROSS_GAPI_INTERFACE_H_ + +#include "gpu/command_buffer/common/constants.h" +#include "gpu/command_buffer/common/resource.h" +#include "gpu/command_buffer/common/o3d_cmd_format.h" + +namespace command_buffer { +namespace o3d { + +// RBGA color definition. +struct RGBA { + float red; + float green; + float blue; + float alpha; +}; + +// This class defines the low-level graphics API, as a pure interface class. +class GAPIInterface { + public: + typedef parse_error::ParseError ParseError; + + GAPIInterface() {} + virtual ~GAPIInterface() {} + + // Initializes the graphics context. + // Returns: + // true if successful. + virtual bool Initialize() = 0; + + // Destroys the graphics context. + virtual void Destroy() = 0; + + // Starts a frame. Rendering should occur between BeginFrame and EndFrame. + virtual void BeginFrame() = 0; + + // Ends the frame, and bring the back buffer to front. Rendering should occur + // between BeginFrame and EndFrame. + virtual void EndFrame() = 0; + + // Clear buffers, filling them with a constant value. + // Parameters: + // buffers: which buffers to clear. Can be a combination (bitwise or) of + // values from ClearBuffer. + // color: the RGBA color to clear the color target with. + // depth: the depth to clear the depth buffer with. + // stencil: the stencil value to clear the stencil buffer with. + virtual void Clear(unsigned int buffers, + const RGBA &color, + float depth, + unsigned int stencil) = 0; + + // Creates a vertex buffer. + // Parameters: + // id: the resource ID for the new vertex buffer. + // size: the size of the vertex buffer, in bytes. + // flags: the vertex buffer flags, as a combination of vertex_buffer::Flags + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateVertexBuffer(ResourceId id, + unsigned int size, + unsigned int flags) = 0; + + // Destroys a vertex buffer. + // Parameters: + // id: the resource ID of the vertex buffer. + // Returns: + // parse_error::kParseInvalidArguments if an invalid vertex buffer + // ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyVertexBuffer(ResourceId id) = 0; + + // Sets data into a vertex buffer. + // Parameters: + // id: the resource ID of the vertex buffer. + // offset: the offset into the vertex buffer where to place the data. + // size: the size of the data. + // data: the source data. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments were + // passed: invalid resource ID, or offset or size out of range. + // parse_error::kParseNoError otherwise. + virtual ParseError SetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data) = 0; + + // Gets data from a vertex buffer. + // Parameters: + // id: the resource ID of the vertex buffer. + // offset: the offset into the vertex buffer where to get the data. + // size: the size of the data. + // data: the destination buffer. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments were + // passed: invalid resource ID, or offset or size out of range. + // parse_error::kParseNoError otherwise. + virtual ParseError GetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data) = 0; + + // Creates an index buffer. + // Parameters: + // id: the resource ID for the new index buffer. + // size: the size of the index buffer, in bytes. + // flags: the index buffer flags, as a combination of index_buffer::Flags. + // Note that indices are 16 bits unless the index_buffer::INDEX_32BIT + // flag is specified. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateIndexBuffer(ResourceId id, + unsigned int size, + unsigned int flags) = 0; + + // Destroys an index buffer. + // Parameters: + // id: the resource ID of the index buffer. + // Returns: + // parse_error::kParseInvalidArguments if an invalid index buffer + // ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyIndexBuffer(ResourceId id) = 0; + + // Sets data into an index buffer. + // Parameters: + // id: the resource ID of the index buffer. + // offset: the offset into the index buffer where to place the data. + // size: the size of the data. + // data: the source data. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments were + // passed: invalid resource ID, or offset or size out of range. + // parse_error::kParseNoError otherwise. + virtual ParseError SetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data) = 0; + + // Gets data from an index buffer. + // Parameters: + // id: the resource ID of the index buffer. + // offset: the offset into the index buffer where to get the data. + // size: the size of the data. + // data: the destination buffer. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments were + // passed: invalid resource ID, or offset or size out of range. + // parse_error::kParseNoError otherwise. + virtual ParseError GetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data) = 0; + + // Creates a vertex struct. A vertex struct describes the input vertex + // attribute streams. + // Parameters: + // id: the resource ID of the vertex struct. + // input_count: the number of input vertex attributes. + // returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateVertexStruct(ResourceId id, + unsigned int input_count) = 0; + + // Destroys a vertex struct. + // Parameters: + // id: the resource ID of the vertex struct. + // Returns: + // parse_error::kParseInvalidArguments if an invalid vertex struct + // ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyVertexStruct(ResourceId id) = 0; + + // Sets an input into a vertex struct. + // Parameters: + // vertex_struct_id: the resource ID of the vertex struct. + // input_index: the index of the input being set. + // vertex_buffer_id: the resource ID of the vertex buffer containing the + // data. + // offset: the offset into the vertex buffer of the input data, in bytes. + // stride: the stride of the input data, in bytes. + // type: the type of the input data. + // semantic: the semantic of the input. + // semantic_index: the semantic index of the input. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError SetVertexInput(ResourceId vertex_struct_id, + unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index) = 0; + + // Sets the current vertex struct for drawing. + // Parameters: + // id: the resource ID of the vertex struct. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed (invalid vertex struct), parse_error::kParseNoError + // otherwise. + virtual ParseError SetVertexStruct(ResourceId id) = 0; + + // Draws primitives, using the current vertex struct and the current effect. + // Parameters: + // primitive_type: the type of primitive to draw. + // first: the index of the first vertex. + // count: the number of primitives to draw. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError Draw(PrimitiveType primitive_type, + unsigned int first, + unsigned int count) = 0; + + // Draws primitives, using the current vertex struct and the current effect, + // as well as an index buffer. + // Parameters: + // primitive_type: the type of primitive to draw. + // index_buffer_id: the resource ID of the index buffer. + // first: the index into the index buffer of the first index to draw. + // count: the number of primitives to draw. + // min_index: the lowest index being drawn. + // max_index: the highest index being drawn. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError DrawIndexed(PrimitiveType primitive_type, + ResourceId index_buffer_id, + unsigned int first, + unsigned int count, + unsigned int min_index, + unsigned int max_index) = 0; + + // Creates an effect, from source code. + // Parameters: + // id: the resource ID of the effect. + // size: the size of data. + // data: the source code for the effect. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed or the effect failed to compile, + // parse_error::kParseNoError otherwise. + virtual ParseError CreateEffect(ResourceId id, + unsigned int size, + const void *data) = 0; + + // Destroys an effect. + // Parameters: + // id: the resource ID of the effect. + // Returns: + // parse_error::kParseInvalidArguments if an invalid effect ID + // was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyEffect(ResourceId id) = 0; + + // Sets the active effect for drawing. + // Parameters: + // id: the resource ID of the effect. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError SetEffect(ResourceId id) = 0; + + // Gets the number of parameters in an effect, returning it in a memory + // buffer as a Uint32. + // Parameters: + // id: the resource ID of the effect. + // size: the size of the data buffer. Must be at least 4 (the size of the + // Uint32). + // data: the buffer receiving the data. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError GetParamCount(ResourceId id, + unsigned int size, + void *data) = 0; + + // Creates an effect parameter by index. + // Parameters: + // param_id: the resource ID of the parameter being created. + // effect_id: the resource ID of the effect containing the parameter. + // data_type: the data type for the parameter. Must match the data type in + // the effect source. + // index: the index of the parameter. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, such as invalid effect ID, unmatching data type or invalid + // index, parse_error::kParseNoError otherwise. + virtual ParseError CreateParam(ResourceId param_id, + ResourceId effect_id, + unsigned int index) = 0; + + // Creates an effect parameter by name. + // Parameters: + // param_id: the resource ID of the parameter being created. + // effect_id: the resource ID of the effect containing the parameter. + // data_type: the data type for the parameter. Must match the data type in + // the effect source. + // size: the size of the parameter name. + // name: the parameter name, as an array of char. Doesn't have to be + // nul-terminated (though nul will terminate the string). + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, such as invalid effect ID, unmatching data type or no parameter + // was found with this name, parse_error::kParseNoError otherwise. + virtual ParseError CreateParamByName(ResourceId param_id, + ResourceId effect_id, + unsigned int size, + const void *name) = 0; + + // Destroys an effect parameter. + // Parameters: + // id: the resource ID of the parameter. + // Returns: + // parse_error::kParseInvalidArguments if an invalid parameter ID + // was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyParam(ResourceId id) = 0; + + // Sets the effect parameter data. + // Parameters: + // id: the resource ID of the parameter. + // size: the size of the data. Must be at least the size of the parameter + // as described by its type. + // data: the parameter data. + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, such as invalid parameter ID, or unmatching data size, + // parse_error::kParseNoError otherwise. + virtual ParseError SetParamData(ResourceId id, + unsigned int size, + const void *data) = 0; + + // Gets the parameter description, storing it into a memory buffer. The + // parameter is described by a effect_param::Desc structure. The size must be + // at least the size of that structure. The name and semantic fields are only + // filled if the character strings fit into the memory buffer. In any case, + // the size field (in the effect_param::Desc) is filled with the size needed + // to fill in the structure, the name and the semantic (if any). Thus to get + // the complete information, GetParamDesc can be called twice, once to get + // the size, and, after allocating a big enough buffer, again to fill in the + // complete information including the text strings. + // Parameters: + // id: the resource ID of the parameter. + // size: the size of the memory buffer that wil receive the parameter + // description. Must be at least sizeof(effect_param::Desc). + // data: the memory buffer. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, such as invalid parameter ID, or unsufficient data size, + // parse_error::kParseNoError otherwise. Note that + // parse_error::kParseNoError will be returned if the structure + // itself fits, not necessarily the names. To make sure all the information + // is available, the caller should compare the returned size member of the + // effect_param::Desc structure to the size parameter passed in. + virtual ParseError GetParamDesc(ResourceId id, + unsigned int size, + void *data) = 0; + + // Gets the number of input streams for an effect, returning it in a memory + // buffer as a Uint32. + // Parameters: + // id: the resource ID of the effect. + // size: the size of the data buffer. Must be at least 4 (the size of the + // Uint32). + // data: the buffer receiving the data. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError GetStreamCount(ResourceId id, + unsigned int size, + void *data) = 0; + + // Gets the stream semantics, storing them in the data buffer. The stream + // is described by an effect_stream::Desc structure which contains a + // semantic type and a semantic index. + // Parameters: + // id: the resource ID of the effect. + // index: which stream semantic to get + // size: the size of the data buffer. Must be at least 8 (the size of two + // Uint32). + // data: the buffer receiving the data. + virtual ParseError GetStreamDesc(ResourceId id, + unsigned int index, + unsigned int size, + void *data) = 0; + + // Creates a 2D texture resource. + // Parameters: + // id: the resource ID of the texture. + // width: the texture width. Must be positive. + // height: the texture height. Must be positive. + // levels: the number of mipmap levels in the texture, or 0 to use the + // maximum. + // format: the format of the texels in the texture. + // flags: the texture flags, as a combination of texture::Flags. + // enable_render_surfaces: bool for whether to enable render surfaces + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateTexture2D(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) = 0; + + // Creates a 3D texture resource. + // Parameters: + // id: the resource ID of the texture. + // width: the texture width. Must be positive. + // height: the texture height. Must be positive. + // depth: the texture depth. Must be positive. + // levels: the number of mipmap levels in the texture, or 0 to use the + // maximum. + // format: the format of the pixels in the texture. + // flags: the texture flags, as a combination of texture::Flags. + // enable_render_surfaces: bool for whether to enable render surfaces + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateTexture3D(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) = 0; + + // Creates a cube map texture resource. + // Parameters: + // id: the resource ID of the texture. + // side: the texture side length. Must be positive. + // levels: the number of mipmap levels in the texture, or 0 to use the + // maximum. + // format: the format of the pixels in the texture. + // flags: the texture flags, as a combination of texture::Flags. + // enable_render_surfaces: bool for whether to enable render surfaces + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateTextureCube(ResourceId id, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) = 0; + + // Sets texel data into a texture resource. This is a common function for + // each of the texture types, but some restrictions exist based on the + // texture type. The specified rectangle or volume of data, defined by x, y, + // width, height and possibly z and depth must fit into the selected mimmap + // level. Data is encoded by rows of 2D blocks, whose size depends on the + // texel format, usually 1x1 texel, but can be 4x4 for DXT* formats. See + // texture::GetBytesPerBlock, texture::GetBlockSizeX and + // texture::GetBlockSizeY. + // Parameters: + // id: the resource ID of the texture. + // x: the x position of the texel corresponding to the first byte of data. + // y: the y position of the texel corresponding to the first byte of data. + // z: the z position of the texel corresponding to the first byte of data. + // Must be 0 for non-3D textures. + // width: the width of the data rectangle/volume. + // height: the height of the data rectangle/volume. + // depth: the depth of the data volume. Must be 1 for non-3D textures. + // level: the mipmap level to put the data into. + // face: which face of the cube to put the data into. Is ignored for + // non-cube map textures. + // row_pitch: the number of bytes between two consecutive rows of blocks, + // in the source data. + // slice_pitch: the number of bytes between two consecutive slices of + // blocks, in the source data. Is ignored for non-3D textures. + // size: the size of the data. + // data: the texel data. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, for example invalid size, or out-of-bounds rectangle/volume, + // parse_error::kParseNoError otherwise. + virtual ParseError SetTextureData(ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) = 0; + + // Gets texel data from a texture resource. This is a common function for + // each of the texture types, but some restrictions exist based on the + // texture type. The specified rectangle or volume of data, defined by x, y, + // width, height and possibly z and depth must fit into the selected mimmap + // level. Data is encoded by rows of 2D blocks, whose size depends on the + // texel format, usually 1x1 texel, but can be 4x4 for DXT* formats. See + // texture::GetBytesPerBlock, texture::GetBlockSizeX and + // texture::GetBlockSizeY. + // Parameters: + // id: the resource ID of the texture. + // x: the x position of the texel corresponding to the first byte of data. + // y: the y position of the texel corresponding to the first byte of data. + // z: the z position of the texel corresponding to the first byte of data. + // Must be 0 for non-3D textures. + // width: the width of the data rectangle/volume. + // height: the height of the data rectangle/volume. + // depth: the depth of the data volume. Must be 1 for non-3D textures. + // level: the mipmap level to put the data into. + // face: which face of the cube to put the data into. Is ignored for + // non-cube map textures. + // row_pitch: the number of bytes between two consecutive rows of blocks, + // in the destination buffer. + // slice_pitch: the number of bytes between two consecutive slices of + // blocks, in the destination buffer. Is ignored for non-3D textures. + // size: the size of the data. + // data: the destination buffer. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, for example invalid size, or out-of-bounds rectangle/volume, + // parse_error::kParseNoError otherwise. + virtual ParseError GetTextureData(ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) = 0; + + // Destroys a texture resource. + // Parameters: + // id: the resource ID of the texture. + // Returns: + // parse_error::kParseInvalidArguments if an invalid texture + // resource ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyTexture(ResourceId id) = 0; + + // Creates a sampler resource. + // Parameters: + // id: the resource ID of the sampler. + // Returns: + // parse_error::kParseNoError. + virtual ParseError CreateSampler(ResourceId id) = 0; + + // Destroys a sampler resource. + // Parameters: + // id: the resource ID of the sampler. + // Returns: + // parse_error::kParseInvalidArguments if an invalid sampler + // resource ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroySampler(ResourceId id) = 0; + + // Sets the states in a sampler resource. + // Parameters: + // id: the resource ID of the sampler. + // addressing_u: the addressing mode for the U coordinate. + // addressing_v: the addressing mode for the V coordinate. + // addressing_w: the addressing mode for the W coordinate. + // mag_filter: the filtering mode when magnifying textures. + // min_filter: the filtering mode when minifying textures. + // mip_filter: the filtering mode for mip-map interpolation textures. + // max_anisotropy: the maximum anisotropy. + // Returns: + // parse_error::kParseInvalidArguments if an invalid sampler + // resource ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError SetSamplerStates(ResourceId id, + sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy) = 0; + + // Sets the color of border pixels. + // Parameters: + // id: the resource ID of the sampler. + // color: the border color. + // Returns: + // parse_error::kParseInvalidArguments if an invalid sampler + // resource ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError SetSamplerBorderColor(ResourceId id, + const RGBA &color) = 0; + + // Sets the texture resource used by a sampler resource. + // Parameters: + // id: the resource ID of the sampler. + // texture_id: the resource id of the texture. + // Returns: + // parse_error::kParseInvalidArguments if an invalid sampler + // resource ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError SetSamplerTexture(ResourceId id, + ResourceId texture_id) = 0; + + // Sets the viewport, and depth range. + // Parameters: + // x, y: upper left corner of the viewport. + // width, height: dimensions of the viewport. + // z_min, z_max: depth range. + virtual void SetViewport(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height, + float z_min, + float z_max) = 0; + + // Sets the scissor test enable flag and rectangle. + // Parameters: + // enable: whether or not scissor test is enabled. + // x, y: upper left corner of the scissor rectangle. + // width, height: dimensions of the scissor rectangle. + virtual void SetScissor(bool enable, + unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height) = 0; + + // Sets the point and line rasterization state. + // Parameters: + // line_smooth: Whether or not line anti-aliasing is enabled. + // point_sprite: Whether or not point sprites are enabled. + // point_size: The point size. + virtual void SetPointLineRaster(bool line_smooth, + bool point_sprite, + float point_size) = 0; + + // Sets the polygon rasterization state. + // Parameters: + // fill_mode: The polygon filling mode. + // cull_mode: The polygon face culling mode. + virtual void SetPolygonRaster(PolygonMode fill_mode, + FaceCullMode cull_mode) = 0; + + // Sets the polygon offset state. Polygon offset is enabled if slope_factor + // or units is not 0. + // The applied offset (in window coordinates) is: + // o = max_slope * slope_factor + r * units + // Where max_slope is the maximum slope of the polygon (in window + // coordinates again), and r is the minimum resolvable z unit. + // Parameters: + // slope_factor: slope factor for the offset. + // units: constant factor for the offset. + virtual void SetPolygonOffset(float slope_factor, float units) = 0; + + // Sets the alpha test states. + // Parameters: + // enable: alpha test enable state. + // reference: reference value for comparison. + // comp: alpha comparison function. + virtual void SetAlphaTest(bool enable, + float reference, + Comparison comp) = 0; + + // Sets the depth test states. + // Note: if the depth test is disabled, z values are not written to the z + // buffer (i.e enable/kAlways is different from disable/*). + // Parameters: + // enable: depth test enable state. + // write_enable: depth write enable state. + // comp: depth comparison function. + virtual void SetDepthTest(bool enable, + bool write_enable, + Comparison comp) = 0; + + // Sets the stencil test states. + // Parameters: + // enable: stencil test enable state. + // separate_ccw: whether or not counter-clockwise faces use separate + // functions/operations (2-sided stencil). + // write_mask: stencil write mask. + // compare_mask: stencil compare mask. + // ref: stencil reference value. + // func_ops: stencil test function and operations for both clockwise and + // counter-clockwise faces. This is a bitfield following the following + // description (little-endian addressing): + // bits 0 - 11: clockwise functions/operations + // bits 12 - 15: must be 0. + // bits 16 - 28: counter-clockwise functions/operations + // bits 29 - 32: must be 0. + virtual void SetStencilTest(bool enable, + bool separate_ccw, + unsigned int write_mask, + unsigned int compare_mask, + unsigned int ref, + Uint32 func_ops) = 0; + + // Sets the color write paramters. + // Parameters: + // red: enable red write. + // green: enable green write. + // blue: enable blue write. + // alpha: enable alpha write. + // dither: enable dithering. + virtual void SetColorWrite(bool red, + bool green, + bool blue, + bool alpha, + bool dither) = 0; + + // Sets the blending mode. + // Parameters: + // enable: whether or not to enable blending. + // separate_alpha: whether or not alpha uses separate Equation/Functions + // (if false, it uses the color ones). + // color_eq: the equation for blending of color values. + // color_src_func: the source function for blending of color values. + // color_dst_func: the destination function for blending of color values. + // alpha_eq: the equation for blending of alpha values. + // alpha_src_func: the source function for blending of alpha values. + // alpha_dst_func: the destination function for blending of alpha values. + virtual void SetBlending(bool enable, + bool separate_alpha, + BlendEq color_eq, + BlendFunc color_src_func, + BlendFunc color_dst_func, + BlendEq alpha_eq, + BlendFunc alpha_src_func, + BlendFunc alpha_dst_func) = 0; + + // Sets the blending color. + virtual void SetBlendingColor(const RGBA &color) = 0; + + // Creates a render surface resource. + // Parameters: + // id: the resource ID of the render surface. + // width: the texture width. Must be positive. + // height: the texture height. Must be positive. + // texture_id: the resource id of the texture. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateRenderSurface(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int mip_level, + unsigned int side, + ResourceId texture_id) = 0; + + // Destroys a render surface resource. + // Parameters: + // id: the resource ID of the render surface. + // Returns: + // parse_error::kParseInvalidArguments if an invalid render + // surface + // resource ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyRenderSurface(ResourceId id) = 0; + + // Creates a depth stencil surface resource. + // Parameters: + // id: the resource ID of the depth stencil surface. + // width: the texture width. Must be positive. + // height: the texture height. Must be positive. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError CreateDepthSurface(ResourceId id, + unsigned int width, + unsigned int height) = 0; + + // Destroys a depth stencil surface resource. + // Parameters: + // id: the resource ID of the depth stencil surface. + // Returns: + // parse_error::kParseInvalidArguments if an invalid render + // surface + // resource ID was passed, parse_error::kParseNoError otherwise. + virtual ParseError DestroyDepthSurface(ResourceId id) = 0; + + // Switches the render surface and depth stencil surface to those + // corresponding to the passed in IDs. + // Parameters: + // render_surface_id: the resource ID of the render surface. + // depth_stencil_id: the resource ID of the render depth stencil surface. + // Returns: + // parse_error::kParseInvalidArguments if invalid arguments are + // passed, parse_error::kParseNoError otherwise. + virtual ParseError SetRenderSurface(ResourceId render_surface_id, + ResourceId depth_stencil_id) = 0; + + // Switch the render surface and depth stencil surface back to the main + // surfaces stored in the render + virtual void SetBackSurfaces() = 0; +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_GAPI_INTERFACE_H_ diff --git a/o3d/gpu/command_buffer/common/logging.h b/o3d/gpu/command_buffer/common/logging.h new file mode 100644 index 0000000..a9bbad8 --- /dev/null +++ b/o3d/gpu/command_buffer/common/logging.h @@ -0,0 +1,67 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file abstracts differences in logging between NaCl and host +// environment. + +#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_LOGGING_H_ +#define GPU_COMMAND_BUFFER_COMMON_CROSS_LOGGING_H_ + +#ifndef __native_client__ +#include "base/logging.h" +#else +#include <sstream> + +// TODO: implement logging through nacl's debug service runtime if +// available. +#define CHECK(X) do {} while (0) +#define CHECK_EQ(X, Y) do {} while (0) +#define CHECK_NE(X, Y) do {} while (0) +#define CHECK_GT(X, Y) do {} while (0) +#define CHECK_GE(X, Y) do {} while (0) +#define CHECK_LT(X, Y) do {} while (0) +#define CHECK_LE(X, Y) do {} while (0) + +#define DCHECK(X) do {} while (0) +#define DCHECK_EQ(X, Y) do {} while (0) +#define DCHECK_NE(X, Y) do {} while (0) +#define DCHECK_GT(X, Y) do {} while (0) +#define DCHECK_GE(X, Y) do {} while (0) +#define DCHECK_LT(X, Y) do {} while (0) +#define DCHECK_LE(X, Y) do {} while (0) + +#define LOG(LEVEL) if (0) std::ostringstream() +#define DLOG(LEVEL) if (0) std::ostringstream() + +#endif + +#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_LOGGING_H_ diff --git a/o3d/gpu/command_buffer/common/o3d_cmd_format.cc b/o3d/gpu/command_buffer/common/o3d_cmd_format.cc new file mode 100644 index 0000000..c70dde7 --- /dev/null +++ b/o3d/gpu/command_buffer/common/o3d_cmd_format.cc @@ -0,0 +1,57 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the binary format definition of the command buffer and +// command buffer commands. + +#include "gpu/command_buffer/common/o3d_cmd_format.h" + +namespace command_buffer { +namespace o3d { + +const char* GetCommandName(CommandId command_id) { + static const char* const names[] = { + #define O3D_COMMAND_BUFFER_CMD_OP(name) # name, + + O3D_COMMAND_BUFFER_CMDS(O3D_COMMAND_BUFFER_CMD_OP) + + #undef O3D_COMMAND_BUFFER_CMD_OP + }; + + int id = static_cast<int>(command_id); + return (id > kStartPoint && id < kNumCommands) ? + names[id - kStartPoint - 1] : "*unknown-command*"; +} + +} // namespace o3d +} // namespace command_buffer + diff --git a/o3d/gpu/command_buffer/common/o3d_cmd_format.h b/o3d/gpu/command_buffer/common/o3d_cmd_format.h new file mode 100644 index 0000000..86503f4 --- /dev/null +++ b/o3d/gpu/command_buffer/common/o3d_cmd_format.h @@ -0,0 +1,3155 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the binary format definition of the command buffer and +// command buffer commands. +// It is recommended you use the CommandBufferHelper object to create commands +// but if you want to go lower level you can use the structures here to help. +// +// A few ways to use them: +// +// Fill out a structure in place: +// +// cmd::SetViewport::Set(ptrToSomeSharedMemory, +// left, right, width, height, z_min, z_max); +// +// Fill out consecutive commands: +// +// Note that each cmd::XXX::Set function returns a pointer to the place +// the next command should go. +// +// void* dest = ptrToSomeSharedMemory; +// dest = cmd::SetViewport::Set(dest, left, right, width, height, min, max); +// dest = cmd::Clear::Set(dest, buffers, r, g, b, a, depth, stencil); +// dest = cmd::Draw::Set(dest, primitive_type, first, count); +// +// NOTE: The types in this file must be POD types. That means they can not have +// constructors, destructors, virtual functions or inheritance and they can only +// use other POD types or intrinsics as members. + +#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_FORMAT_H_ +#define GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_FORMAT_H_ + +#include "gpu/command_buffer/common/cmd_buffer_common.h" +#include "gpu/command_buffer/common/resource.h" + +namespace command_buffer { +namespace o3d { + +// This macro is used to safely and convienently expand the list of commnad +// buffer commands in to various lists and never have them get out of sync. To +// add a new command, add it this list, create the corresponding structure below +// and then add a function in gapi_decoder.cc called Handle_COMMAND_NAME where +// COMMAND_NAME is the name of your command structure. +// +// NOTE: THE ORDER OF THESE MUST NOT CHANGE (their id is derived by order) +#define O3D_COMMAND_BUFFER_CMDS(OP) \ + OP(BeginFrame) /* 1024 */ \ + OP(EndFrame) /* 1025 */ \ + OP(Clear) /* 1026 */ \ + OP(CreateVertexBuffer) /* 1027 */ \ + OP(DestroyVertexBuffer) /* 1028 */ \ + OP(SetVertexBufferData) /* 1029 */ \ + OP(SetVertexBufferDataImmediate) /* 1030 */ \ + OP(GetVertexBufferData) /* 1031 */ \ + OP(CreateIndexBuffer) /* 1032 */ \ + OP(DestroyIndexBuffer) /* 1033 */ \ + OP(SetIndexBufferData) /* 1034 */ \ + OP(SetIndexBufferDataImmediate) /* 1035 */ \ + OP(GetIndexBufferData) /* 1036 */ \ + OP(CreateVertexStruct) /* 1037 */ \ + OP(DestroyVertexStruct) /* 1038 */ \ + OP(SetVertexInput) /* 1039 */ \ + OP(SetVertexStruct) /* 1040 */ \ + OP(Draw) /* 1041 */ \ + OP(DrawIndexed) /* 1042 */ \ + OP(CreateEffect) /* 1043 */ \ + OP(CreateEffectImmediate) /* 1044 */ \ + OP(DestroyEffect) /* 1045 */ \ + OP(SetEffect) /* 1046 */ \ + OP(GetParamCount) /* 1047 */ \ + OP(CreateParam) /* 1048 */ \ + OP(CreateParamByName) /* 1049 */ \ + OP(CreateParamByNameImmediate) /* 1050 */ \ + OP(DestroyParam) /* 1051 */ \ + OP(SetParamData) /* 1052 */ \ + OP(SetParamDataImmediate) /* 1053 */ \ + OP(GetParamDesc) /* 1054 */ \ + OP(GetStreamCount) /* 1055 */ \ + OP(GetStreamDesc) /* 1056 */ \ + OP(DestroyTexture) /* 1057 */ \ + OP(CreateTexture2d) /* 1058 */ \ + OP(CreateTexture3d) /* 1059 */ \ + OP(CreateTextureCube) /* 1060 */ \ + OP(SetTextureData) /* 1061 */ \ + OP(SetTextureDataImmediate) /* 1062 */ \ + OP(GetTextureData) /* 1063 */ \ + OP(CreateSampler) /* 1064 */ \ + OP(DestroySampler) /* 1065 */ \ + OP(SetSamplerStates) /* 1066 */ \ + OP(SetSamplerBorderColor) /* 1067 */ \ + OP(SetSamplerTexture) /* 1068 */ \ + OP(SetViewport) /* 1069 */ \ + OP(SetScissor) /* 1070 */ \ + OP(SetPointLineRaster) /* 1071 */ \ + OP(SetPolygonRaster) /* 1072 */ \ + OP(SetPolygonOffset) /* 1073 */ \ + OP(SetAlphaTest) /* 1074 */ \ + OP(SetDepthTest) /* 1075 */ \ + OP(SetStencilTest) /* 1076 */ \ + OP(SetBlending) /* 1077 */ \ + OP(SetBlendingColor) /* 1078 */ \ + OP(SetColorWrite) /* 1079 */ \ + OP(CreateRenderSurface) /* 1080 */ \ + OP(DestroyRenderSurface) /* 1081 */ \ + OP(CreateDepthSurface) /* 1082 */ \ + OP(DestroyDepthSurface) /* 1083 */ \ + OP(SetRenderSurface) /* 1084 */ \ + OP(SetBackSurfaces) /* 1085 */ \ + + +// GAPI commands. +enum CommandId { + kStartPoint = cmd::kLastCommonId, // All O3D commands start after this. + #define GPU_COMMAND_BUFFER_CMD_OP(name) k ## name, + + O3D_COMMAND_BUFFER_CMDS(GPU_COMMAND_BUFFER_CMD_OP) + + #undef GPU_COMMAND_BUFFER_CMD_OP + + kNumCommands, +}; + +// Bit definitions for buffers to clear. +enum ClearBuffer { + kColor = 0x1, + kDepth = 0x2, + kStencil = 0x4, + kAllBuffers = kColor | kDepth | kStencil +}; + +// Polygon mode for SetPolygonRaster +enum PolygonMode { + kPolygonModePoints, + kPolygonModeLines, + kPolygonModeFill, + kNumPolygonMode +}; + +// Face culling mode for SetPolygonRaster +enum FaceCullMode { + kCullNone, + kCullCW, + kCullCCW, + kNumFaceCullMode +}; + +// Primitive type for Draw and DrawIndexed. +enum PrimitiveType { + kPoints, + kLines, + kLineStrips, + kTriangles, + kTriangleStrips, + kTriangleFans, + kMaxPrimitiveType +}; + +// Comparison function for alpha or depth test +enum Comparison { + kNever, + kLess, + kEqual, + kLEqual, + kGreater, + kNotEqual, + kGEqual, + kAlways, + kNumComparison +}; + +// Stencil operation +enum StencilOp { + kKeep, + kZero, + kReplace, + kIncNoWrap, + kDecNoWrap, + kInvert, + kIncWrap, + kDecWrap, + kNumStencilOp +}; + +// Blend Equation +enum BlendEq { + kBlendEqAdd, + kBlendEqSub, + kBlendEqRevSub, + kBlendEqMin, + kBlendEqMax, + kNumBlendEq +}; + +// Blend Funtion +enum BlendFunc { + kBlendFuncZero, + kBlendFuncOne, + kBlendFuncSrcColor, + kBlendFuncInvSrcColor, + kBlendFuncSrcAlpha, + kBlendFuncInvSrcAlpha, + kBlendFuncDstAlpha, + kBlendFuncInvDstAlpha, + kBlendFuncDstColor, + kBlendFuncInvDstColor, + kBlendFuncSrcAlphaSaturate, + kBlendFuncBlendColor, + kBlendFuncInvBlendColor, + kNumBlendFunc +}; + +const char* GetCommandName(CommandId id); + +// Make sure the compiler does not add extra padding to any of the command +// structures. +#pragma pack(push, 1) + +struct BeginFrame { + typedef BeginFrame ValueType; + static const CommandId kCmdId = kBeginFrame; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init() { + SetHeader(); + } + static void* Set(void* cmd) { + static_cast<ValueType*>(cmd)->Init(); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; +}; + +COMPILE_ASSERT(sizeof(BeginFrame) == 4, Sizeof_BeginFrame_is_not_4); +COMPILE_ASSERT(offsetof(BeginFrame, header) == 0, + OffsetOf_BeginFrame_header_not_0); + +struct EndFrame { + typedef EndFrame ValueType; + static const CommandId kCmdId = kEndFrame; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init() { + SetHeader(); + } + static void* Set(void* cmd) { + static_cast<ValueType*>(cmd)->Init(); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; +}; + +COMPILE_ASSERT(sizeof(EndFrame) == 4, Sizeof_EndFrame_is_not_4); +COMPILE_ASSERT(offsetof(EndFrame, header) == 0, + OffsetOf_EndFrame_header_not_0); + +struct Clear { + typedef Clear ValueType; + static const CommandId kCmdId = kClear; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(uint32 _buffers, float _red, float _green, float _blue, + float _alpha, float _depth, uint32 _stencil) { + SetHeader(); + buffers = _buffers; + red = _red; + green = _green; + blue = _blue; + alpha = _alpha; + depth = _depth; + stencil = _stencil; + } + + static void* Set(void* cmd, uint32 buffers, + float red, float green, float blue, float alpha, + float depth, + uint32 stencil) { + static_cast<ValueType*>(cmd)->Init( + buffers, red, green, blue, alpha, depth, stencil); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + uint32 buffers; + float red; + float green; + float blue; + float alpha; + float depth; + uint32 stencil; +}; + +COMPILE_ASSERT(sizeof(Clear) == 32, Sizeof_Clear_is_not_32); +COMPILE_ASSERT(offsetof(Clear, header) == 0, + OffsetOf_Clear_header_not_0); +COMPILE_ASSERT(offsetof(Clear, buffers) == 4, + OffsetOf_Clear_buffers_not_4); +COMPILE_ASSERT(offsetof(Clear, red) == 8, + OffsetOf_Clear_red_not_8); +COMPILE_ASSERT(offsetof(Clear, green) == 12, + OffsetOf_Clear_green_not_12); +COMPILE_ASSERT(offsetof(Clear, blue) == 16, + OffsetOf_Clear_blue_not_16); +COMPILE_ASSERT(offsetof(Clear, alpha) == 20, + OffsetOf_Clear_alpha_not_20); +COMPILE_ASSERT(offsetof(Clear, depth) == 24, + OffsetOf_Clear_depth_not_24); +COMPILE_ASSERT(offsetof(Clear, stencil) == 28, + OffsetOf_Clear_stencil_not_28); + +struct SetViewport { + typedef SetViewport ValueType; + static const CommandId kCmdId = kSetViewport; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init( + uint32 _left, + uint32 _top, + uint32 _width, + uint32 _height, + float _z_min, + float _z_max) { + SetHeader(); + left = _left; + top = _top; + width = _width; + height = _height; + z_min = _z_min; + z_max = _z_max; + } + + static void* Set(void* cmd, + uint32 left, + uint32 top, + uint32 width, + uint32 height, + float z_min, + float z_max) { + static_cast<ValueType*>(cmd)->Init( + left, + top, + width, + height, + z_min, + z_max); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + uint32 left; + uint32 top; + uint32 width; + uint32 height; + float z_min; + float z_max; +}; + +COMPILE_ASSERT(sizeof(SetViewport) == 28, Sizeof_SetViewport_is_not_28); +COMPILE_ASSERT(offsetof(SetViewport, header) == 0, + OffsetOf_SetViewport_header_not_0); +COMPILE_ASSERT(offsetof(SetViewport, left) == 4, + OffsetOf_SetViewport_left_not_4); +COMPILE_ASSERT(offsetof(SetViewport, top) == 8, + OffsetOf_SetViewport_top_not_8); +COMPILE_ASSERT(offsetof(SetViewport, width) == 12, + OffsetOf_SetViewport_width_not_12); +COMPILE_ASSERT(offsetof(SetViewport, height) == 16, + OffsetOf_SetViewport_height_not_16); +COMPILE_ASSERT(offsetof(SetViewport, z_min) == 20, + OffsetOf_SetViewport_z_min_not_20); +COMPILE_ASSERT(offsetof(SetViewport, z_max) == 24, + OffsetOf_SetViewport_z_max_not_24); + +struct CreateVertexBuffer { + typedef CreateVertexBuffer ValueType; + static const CommandId kCmdId = kCreateVertexBuffer; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_buffer_id, uint32 _size, + vertex_buffer::Flags _flags) { + SetHeader(); + vertex_buffer_id = _vertex_buffer_id; + size = _size; + flags = static_cast<uint32>(_flags); + } + + static void* Set(void* cmd, ResourceId vertex_buffer_id, + uint32 size, vertex_buffer::Flags flags) { + static_cast<ValueType*>(cmd)->Init(vertex_buffer_id, size, flags); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_buffer_id; + uint32 size; + uint32 flags; +}; + +COMPILE_ASSERT(sizeof(CreateVertexBuffer) == 16, + Sizeof_CreateVertexBuffer_is_not_16); +COMPILE_ASSERT(offsetof(CreateVertexBuffer, header) == 0, + OffsetOf_CreateVertexBuffer_header_not_0); +COMPILE_ASSERT(offsetof(CreateVertexBuffer, vertex_buffer_id) == 4, + OffsetOf_CreateVertexBuffer_vertex_buffer_id_not_4); +COMPILE_ASSERT(offsetof(CreateVertexBuffer, size) == 8, + OffsetOf_CreateVertexBuffer_size_not_8); +COMPILE_ASSERT(offsetof(CreateVertexBuffer, flags) == 12, + OffsetOf_CreateVertexBuffer_flags_not_12); + +struct DestroyVertexBuffer { + typedef DestroyVertexBuffer ValueType; + static const CommandId kCmdId = kDestroyVertexBuffer; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_buffer_id) { + SetHeader(); + vertex_buffer_id = _vertex_buffer_id; + } + + static void* Set(void* cmd, ResourceId vertex_buffer_id) { + static_cast<ValueType*>(cmd)->Init(vertex_buffer_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_buffer_id; +}; + +COMPILE_ASSERT(sizeof(DestroyVertexBuffer) == 8, + Sizeof_DestroyVertexBuffer_is_not_8); +COMPILE_ASSERT(offsetof(DestroyVertexBuffer, header) == 0, + OffsetOf_DestroyVertexBuffer_header_not_0); +COMPILE_ASSERT(offsetof(DestroyVertexBuffer, vertex_buffer_id) == 4, + OffsetOf_DestroyVertexBuffer_vertex_buffer_id_not_4); + +struct SetVertexBufferDataImmediate { + typedef SetVertexBufferDataImmediate ValueType; + static const CommandId kCmdId = kSetVertexBufferDataImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + void SetHeader(uint32 size) { + header.SetCmdBySize<ValueType>(size); + } + + void Init(ResourceId _vertex_buffer_id, uint32 _offset, + const void* data, uint32 size) { + SetHeader(size); + vertex_buffer_id = _vertex_buffer_id; + offset = _offset; + memcpy(ImmediateDataAddress(this), data, size); + } + + static void* Set(void* cmd, ResourceId vertex_buffer_id, uint32 offset, + const void* data, uint32 size) { + static_cast<ValueType*>(cmd)->Init(vertex_buffer_id, offset, data, size); + return NextImmediateCmdAddress<ValueType>(cmd, size); + } + + CommandHeader header; + ResourceId vertex_buffer_id; + uint32 offset; +}; + +COMPILE_ASSERT(sizeof(SetVertexBufferDataImmediate) == 12, + Sizeof_SetVertexBufferDataImmediate_is_not_12); +COMPILE_ASSERT(offsetof(SetVertexBufferDataImmediate, header) == 0, + OffsetOf_SetVertexBufferDataImmediate_header_not_0); +COMPILE_ASSERT(offsetof(SetVertexBufferDataImmediate, vertex_buffer_id) == 4, + OffsetOf_SetVertexBufferDataImmediate_vertex_buffer_id_not_4); +COMPILE_ASSERT(offsetof(SetVertexBufferDataImmediate, offset) == 8, + OffsetOf_SetVertexBufferDataImmediate_offset_not_8); + +struct SetVertexBufferData { + typedef SetVertexBufferData ValueType; + static const CommandId kCmdId = kSetVertexBufferData; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_buffer_id, uint32 _offset, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + vertex_buffer_id = _vertex_buffer_id; + offset = _offset; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId vertex_buffer_id, + uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(vertex_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_buffer_id; + uint32 offset; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(SetVertexBufferData) == 24, + Sizeof_SetVertexBufferData_is_not_24); +COMPILE_ASSERT(offsetof(SetVertexBufferData, header) == 0, + OffsetOf_SetVertexBufferData_header_not_0); +COMPILE_ASSERT(offsetof(SetVertexBufferData, vertex_buffer_id) == 4, + OffsetOf_SetVertexBufferData_vertex_buffer_id_not_4); +COMPILE_ASSERT(offsetof(SetVertexBufferData, offset) == 8, + OffsetOf_SetVertexBufferData_offset_not_8); +COMPILE_ASSERT(offsetof(SetVertexBufferData, size) == 12, + OffsetOf_SetVertexBufferData_size_not_12); +COMPILE_ASSERT(offsetof(SetVertexBufferData, shared_memory) == 16, + OffsetOf_SetVertexBufferData_shared_memory_not_16); + +struct GetVertexBufferData { + typedef GetVertexBufferData ValueType; + static const CommandId kCmdId = kGetVertexBufferData; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_buffer_id, uint32 _offset, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + vertex_buffer_id = _vertex_buffer_id; + offset = _offset; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId vertex_buffer_id, + uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(vertex_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_buffer_id; + uint32 offset; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(GetVertexBufferData) == 24, + Sizeof_GetVertexBufferData_is_not_24); +COMPILE_ASSERT(offsetof(GetVertexBufferData, header) == 0, + OffsetOf_GetVertexBufferData_header_not_0); +COMPILE_ASSERT(offsetof(GetVertexBufferData, vertex_buffer_id) == 4, + OffsetOf_GetVertexBufferData_vertex_buffer_id_not_4); +COMPILE_ASSERT(offsetof(GetVertexBufferData, offset) == 8, + OffsetOf_GetVertexBufferData_offset_not_8); +COMPILE_ASSERT(offsetof(GetVertexBufferData, size) == 12, + OffsetOf_GetVertexBufferData_size_not_12); +COMPILE_ASSERT(offsetof(GetVertexBufferData, shared_memory) == 16, + OffsetOf_GetVertexBufferData_shared_memory_not_16); + +struct CreateIndexBuffer { + typedef CreateIndexBuffer ValueType; + static const CommandId kCmdId = kCreateIndexBuffer; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _index_buffer_id, uint32 _size, + index_buffer::Flags _flags) { + SetHeader(); + index_buffer_id = _index_buffer_id; + size = _size; + flags = static_cast<uint32>(_flags); + } + + static void* Set(void* cmd, ResourceId index_buffer_id, + uint32 size, index_buffer::Flags flags) { + static_cast<ValueType*>(cmd)->Init(index_buffer_id, size, flags); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId index_buffer_id; + uint32 size; + uint32 flags; +}; + +COMPILE_ASSERT(sizeof(CreateIndexBuffer) == 16, + Sizeof_CreateIndexBuffer_is_not_16); +COMPILE_ASSERT(offsetof(CreateIndexBuffer, header) == 0, + OffsetOf_CreateIndexBuffer_header_not_0); +COMPILE_ASSERT(offsetof(CreateIndexBuffer, index_buffer_id) == 4, + OffsetOf_CreateIndexBuffer_index_buffer_id_not_4); +COMPILE_ASSERT(offsetof(CreateIndexBuffer, size) == 8, + OffsetOf_CreateIndexBuffer_size_not_8); +COMPILE_ASSERT(offsetof(CreateIndexBuffer, flags) == 12, + OffsetOf_CreateIndexBuffer_flags_not_12); + +struct DestroyIndexBuffer { + typedef DestroyIndexBuffer ValueType; + static const CommandId kCmdId = kDestroyIndexBuffer; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _index_buffer_id) { + SetHeader(); + index_buffer_id = _index_buffer_id; + } + + static void* Set(void* cmd, ResourceId index_buffer_id) { + static_cast<ValueType*>(cmd)->Init(index_buffer_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId index_buffer_id; +}; + +COMPILE_ASSERT(sizeof(DestroyIndexBuffer) == 8, + Sizeof_DestroyIndexBuffer_is_not_8); +COMPILE_ASSERT(offsetof(DestroyIndexBuffer, header) == 0, + OffsetOf_DestroyIndexBuffer_header_not_0); +COMPILE_ASSERT(offsetof(DestroyIndexBuffer, index_buffer_id) == 4, + OffsetOf_DestroyIndexBuffer_index_buffer_id_not_4); + +struct SetIndexBufferDataImmediate { + typedef SetIndexBufferDataImmediate ValueType; + static const CommandId kCmdId = kSetIndexBufferDataImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + void SetHeader(uint32 size) { + header.SetCmdBySize<ValueType>(size); + } + + void Init(ResourceId _index_buffer_id, uint32 _offset, + const void* data, uint32 size) { + SetHeader(size); + index_buffer_id = _index_buffer_id; + offset = _offset; + memcpy(ImmediateDataAddress(this), data, size); + } + + static void* Set(void* cmd, ResourceId index_buffer_id, uint32 offset, + const void* data, uint32 size) { + static_cast<ValueType*>(cmd)->Init(index_buffer_id, offset, data, size); + return NextImmediateCmdAddress<ValueType>(cmd, size); + } + + CommandHeader header; + ResourceId index_buffer_id; + uint32 offset; +}; + +COMPILE_ASSERT(sizeof(SetIndexBufferDataImmediate) == 12, + Sizeof_SetIndexBufferDataImmediate_is_not_12); +COMPILE_ASSERT(offsetof(SetIndexBufferDataImmediate, header) == 0, + OffsetOf_SetIndexBufferDataImmediate_header_not_0); +COMPILE_ASSERT(offsetof(SetIndexBufferDataImmediate, index_buffer_id) == 4, + OffsetOf_SetIndexBufferDataImmediate_index_buffer_id_not_4); +COMPILE_ASSERT(offsetof(SetIndexBufferDataImmediate, offset) == 8, + OffsetOf_SetIndexBufferDataImmediate_offset_not_8); + +struct SetIndexBufferData { + typedef SetIndexBufferData ValueType; + static const CommandId kCmdId = kSetIndexBufferData; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _index_buffer_id, uint32 _offset, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + index_buffer_id = _index_buffer_id; + offset = _offset; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, + ResourceId index_buffer_id, uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(index_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId index_buffer_id; + uint32 offset; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(SetIndexBufferData) == 24, + Sizeof_SetIndexBufferData_is_not_24); +COMPILE_ASSERT(offsetof(SetIndexBufferData, header) == 0, + OffsetOf_SetIndexBufferData_header_not_0); +COMPILE_ASSERT(offsetof(SetIndexBufferData, index_buffer_id) == 4, + OffsetOf_SetIndexBufferData_index_buffer_id_not_4); +COMPILE_ASSERT(offsetof(SetIndexBufferData, offset) == 8, + OffsetOf_SetIndexBufferData_offset_not_8); +COMPILE_ASSERT(offsetof(SetIndexBufferData, size) == 12, + OffsetOf_SetIndexBufferData_size_not_12); +COMPILE_ASSERT(offsetof(SetIndexBufferData, shared_memory) == 16, + OffsetOf_SetIndexBufferData_shared_memory_not_16); + +struct GetIndexBufferData { + typedef GetIndexBufferData ValueType; + static const CommandId kCmdId = kGetIndexBufferData; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _index_buffer_id, uint32 _offset, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + index_buffer_id = _index_buffer_id; + offset = _offset; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId index_buffer_id, + uint32 offset, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(index_buffer_id, offset, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId index_buffer_id; + uint32 offset; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(GetIndexBufferData) == 24, + Sizeof_GetIndexBufferData_is_not_24); +COMPILE_ASSERT(offsetof(GetIndexBufferData, header) == 0, + OffsetOf_GetIndexBufferData_header_not_0); +COMPILE_ASSERT(offsetof(GetIndexBufferData, index_buffer_id) == 4, + OffsetOf_GetIndexBufferData_index_buffer_id_not_4); +COMPILE_ASSERT(offsetof(GetIndexBufferData, offset) == 8, + OffsetOf_GetIndexBufferData_offset_not_8); +COMPILE_ASSERT(offsetof(GetIndexBufferData, size) == 12, + OffsetOf_GetIndexBufferData_size_not_12); +COMPILE_ASSERT(offsetof(GetIndexBufferData, shared_memory) == 16, + OffsetOf_GetIndexBufferData_shared_memory_not_16); + +struct CreateVertexStruct { + typedef CreateVertexStruct ValueType; + static const CommandId kCmdId = kCreateVertexStruct; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_struct_id, uint32 _input_count) { + SetHeader(); + vertex_struct_id = _vertex_struct_id; + input_count = _input_count; + } + + static void* Set(void* cmd, ResourceId vertex_struct_id, uint32 input_count) { + static_cast<ValueType*>(cmd)->Init(vertex_struct_id, input_count); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_struct_id; + uint32 input_count; +}; + +COMPILE_ASSERT(sizeof(CreateVertexStruct) == 12, + Sizeof_CreateVertexStruct_is_not_12); +COMPILE_ASSERT(offsetof(CreateVertexStruct, header) == 0, + OffsetOf_CreateVertexStruct_header_not_0); +COMPILE_ASSERT(offsetof(CreateVertexStruct, vertex_struct_id) == 4, + OffsetOf_CreateVertexStruct_vertex_struct_id_not_4); +COMPILE_ASSERT(offsetof(CreateVertexStruct, input_count) == 8, + OffsetOf_CreateVertexStruct_input_count_not_8); + +struct DestroyVertexStruct { + typedef DestroyVertexStruct ValueType; + static const CommandId kCmdId = kDestroyVertexStruct; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_struct_id) { + SetHeader(); + vertex_struct_id = _vertex_struct_id; + } + + static void* Set(void* cmd, ResourceId vertex_struct_id) { + static_cast<ValueType*>(cmd)->Init(vertex_struct_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_struct_id; +}; + +COMPILE_ASSERT(sizeof(DestroyVertexStruct) == 8, + Sizeof_DestroyVertexStruct_is_not_8); +COMPILE_ASSERT(offsetof(DestroyVertexStruct, header) == 0, + OffsetOf_DestroyVertexStruct_header_not_0); +COMPILE_ASSERT(offsetof(DestroyVertexStruct, vertex_struct_id) == 4, + OffsetOf_DestroyVertexStruct_vertex_struct_id_not_4); + +struct SetVertexInput { + typedef SetVertexInput ValueType; + static const CommandId kCmdId = kSetVertexInput; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // type_stride_semantic field. + typedef BitField<0, 4> SemanticIndex; + typedef BitField<4, 4> Semantic; + typedef BitField<8, 8> Type; + typedef BitField<16, 16> Stride; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_struct_id, + uint32 _input_index, + ResourceId _vertex_buffer_id, + uint32 _offset, + vertex_struct::Semantic _semantic, + uint32 _semantic_index, + vertex_struct::Type _type, + uint32 _stride) { + SetHeader(); + vertex_struct_id = _vertex_struct_id; + input_index = _input_index; + vertex_buffer_id = _vertex_buffer_id; + offset = _offset; + type_stride_semantic = + Semantic::MakeValue(_semantic) | + SemanticIndex::MakeValue(_semantic_index) | + Type::MakeValue(_type) | + Stride::MakeValue(_stride); + } + + static void* Set( + void* cmd, + ResourceId vertex_struct_id, + uint32 input_index, + ResourceId vertex_buffer_id, + uint32 offset, + vertex_struct::Semantic semantic, + uint32 semantic_index, + vertex_struct::Type type, + uint32 stride) { + static_cast<ValueType*>(cmd)->Init( + vertex_struct_id, + input_index, + vertex_buffer_id, + offset, + semantic, + semantic_index, + type, + stride); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_struct_id; + uint32 input_index; + ResourceId vertex_buffer_id; + uint32 offset; + uint32 type_stride_semantic; +}; + +COMPILE_ASSERT(sizeof(SetVertexInput) == 24, + Sizeof_SetVertexInput_is_not_24); +COMPILE_ASSERT(offsetof(SetVertexInput, header) == 0, + OffsetOf_SetVertexInput_header_not_0); +COMPILE_ASSERT(offsetof(SetVertexInput, vertex_struct_id) == 4, + OffsetOf_SetVertexInput_vertex_struct_id_not_4); +COMPILE_ASSERT(offsetof(SetVertexInput, input_index) == 8, + OffsetOf_SetVertexInput_input_index_not_8); +COMPILE_ASSERT(offsetof(SetVertexInput, vertex_buffer_id) == 12, + OffsetOf_SetVertexInput_vertex_buffer_id_not_12); +COMPILE_ASSERT(offsetof(SetVertexInput, offset) == 16, + OffsetOf_SetVertexInput_offset_not_16); +COMPILE_ASSERT(offsetof(SetVertexInput, type_stride_semantic) == 20, + OffsetOf_SetVertexInput_type_stride_semantic_not_20); + +struct SetVertexStruct { + typedef SetVertexStruct ValueType; + static const CommandId kCmdId = kSetVertexStruct; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _vertex_struct_id) { + SetHeader(); + vertex_struct_id = _vertex_struct_id; + } + + static void* Set(void* cmd, ResourceId vertex_struct_id) { + static_cast<ValueType*>(cmd)->Init(vertex_struct_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId vertex_struct_id; +}; + +COMPILE_ASSERT(sizeof(SetVertexStruct) == 8, + Sizeof_SetVertexStruct_is_not_8); +COMPILE_ASSERT(offsetof(SetVertexStruct, header) == 0, + OffsetOf_SetVertexStruct_header_not_0); +COMPILE_ASSERT(offsetof(SetVertexStruct, vertex_struct_id) == 4, + OffsetOf_SetVertexStruct_vertex_struct_id_not_4); + +struct Draw { + typedef Draw ValueType; + static const CommandId kCmdId = kDraw; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(PrimitiveType _primitive_type, uint32 _first, uint32 _count) { + SetHeader(); + primitive_type = _primitive_type; + first = _first; + count = _count; + } + + static void* Set(void* cmd, PrimitiveType primitive_type, uint32 first, + uint32 count) { + static_cast<ValueType*>(cmd)->Init(primitive_type, first, count); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + uint32 primitive_type; + uint32 first; + uint32 count; +}; + +COMPILE_ASSERT(sizeof(Draw) == 16, Sizeof_DRAW_is_not_16); +COMPILE_ASSERT(offsetof(Draw, header) == 0, + OffsetOf_Draw_header_not_0); +COMPILE_ASSERT(offsetof(Draw, primitive_type) == 4, + OffsetOf_Draw_primitive_type_not_4); +COMPILE_ASSERT(offsetof(Draw, first) == 8, + OffsetOf_Draw_first_not_8); +COMPILE_ASSERT(offsetof(Draw, count) == 12, + OffsetOf_Draw_count_not_12); + +struct DrawIndexed { + typedef DrawIndexed ValueType; + static const CommandId kCmdId = kDrawIndexed; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init( + PrimitiveType _primitive_type, + ResourceId _index_buffer_id, + uint32 _first, + uint32 _count, + uint32 _min_index, + uint32 _max_index) { + SetHeader(); + primitive_type = _primitive_type; + index_buffer_id = _index_buffer_id; + first = _first; + count = _count; + min_index = _min_index; + max_index = _max_index; + } + + static void* Set(void* cmd, + PrimitiveType primitive_type, + ResourceId index_buffer_id, + uint32 first, + uint32 count, + uint32 min_index, + uint32 max_index) { + static_cast<ValueType*>(cmd)->Init( + primitive_type, + index_buffer_id, + first, + count, + min_index, + max_index); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + uint32 primitive_type; + ResourceId index_buffer_id; + uint32 first; + uint32 count; + uint32 min_index; + uint32 max_index; +}; + +COMPILE_ASSERT(sizeof(DrawIndexed) == 28, Sizeof_DrawIndexed_is_not_28); +COMPILE_ASSERT(offsetof(DrawIndexed, header) == 0, + OffsetOf_DrawIndexed_header_not_0); +COMPILE_ASSERT(offsetof(DrawIndexed, primitive_type) == 4, + OffsetOf_DrawIndexed_primitive_type_not_4); +COMPILE_ASSERT(offsetof(DrawIndexed, index_buffer_id) == 8, + OffsetOf_DrawIndexed_index_buffer_id_not_8); +COMPILE_ASSERT(offsetof(DrawIndexed, first) == 12, + OffsetOf_DrawIndexed_first_not_12); +COMPILE_ASSERT(offsetof(DrawIndexed, count) == 16, + OffsetOf_DrawIndexed_count_not_16); +COMPILE_ASSERT(offsetof(DrawIndexed, min_index) == 20, + OffsetOf_DrawIndexed_min_index_not_20); +COMPILE_ASSERT(offsetof(DrawIndexed, max_index) == 24, + OffsetOf_DrawIndexed_max_index_not_24); + +struct CreateEffect { + typedef CreateEffect ValueType; + static const CommandId kCmdId = kCreateEffect; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _effect_id, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + effect_id = _effect_id; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId effect_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(effect_id, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId effect_id; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(CreateEffect) == 20, Sizeof_CreateEffect_is_not_20); +COMPILE_ASSERT(offsetof(CreateEffect, header) == 0, + OffsetOf_CreateEffect_header_not_0); +COMPILE_ASSERT(offsetof(CreateEffect, effect_id) == 4, + OffsetOf_CreateEffect_effect_id_not_4); +COMPILE_ASSERT(offsetof(CreateEffect, size) == 8, + OffsetOf_CreateEffect_size_not_8); +COMPILE_ASSERT(offsetof(CreateEffect, shared_memory) == 12, + OffsetOf_CreateEffect_shared_memory_not_12); + +struct CreateEffectImmediate { + typedef CreateEffectImmediate ValueType; + static const CommandId kCmdId = kCreateEffectImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + void SetHeader(uint32 size) { + header.SetCmdBySize<ValueType>(size); + } + + void Init(ResourceId _effect_id, uint32 _size, const void* data) { + SetHeader(_size); + effect_id = _effect_id; + size = _size; + } + + static void* Set(void* cmd, + ResourceId effect_id, uint32 size, const void* data) { + static_cast<ValueType*>(cmd)->Init(effect_id, size, data); + return NextImmediateCmdAddress<ValueType>(cmd, size); + } + + CommandHeader header; + ResourceId effect_id; + uint32 size; +}; + +COMPILE_ASSERT(sizeof(CreateEffectImmediate) == 12, + Sizeof_CreateEffectImmediate_is_not_12); +COMPILE_ASSERT(offsetof(CreateEffectImmediate, header) == 0, + OffsetOf_CreateEffectImmediate_header_not_0); +COMPILE_ASSERT(offsetof(CreateEffectImmediate, effect_id) == 4, + OffsetOf_CreateEffectImmediate_effect_id_not_4); +COMPILE_ASSERT(offsetof(CreateEffectImmediate, size) == 8, + OffsetOf_CreateEffectImmediate_size_not_8); + +struct DestroyEffect { + typedef DestroyEffect ValueType; + static const CommandId kCmdId = kDestroyEffect; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _effect_id) { + SetHeader(); + effect_id = _effect_id; + } + + static void* Set(void* cmd, ResourceId effect_id) { + static_cast<ValueType*>(cmd)->Init(effect_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId effect_id; +}; + +COMPILE_ASSERT(sizeof(DestroyEffect) == 8, Sizeof_DestroyEffect_is_not_8); +COMPILE_ASSERT(offsetof(DestroyEffect, header) == 0, + OffsetOf_DestroyEffect_header_not_0); +COMPILE_ASSERT(offsetof(DestroyEffect, effect_id) == 4, + OffsetOf_DestroyEffect_effect_id_not_4); + +struct SetEffect { + typedef SetEffect ValueType; + static const CommandId kCmdId = kSetEffect; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _effect_id) { + SetHeader(); + effect_id = _effect_id; + } + + static void* Set(void* cmd, ResourceId effect_id) { + static_cast<ValueType*>(cmd)->Init(effect_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId effect_id; +}; + +COMPILE_ASSERT(sizeof(SetEffect) == 8, Sizeof_SetEffect_is_not_8); +COMPILE_ASSERT(offsetof(SetEffect, header) == 0, + OffsetOf_SetEffect_header_not_0); +COMPILE_ASSERT(offsetof(SetEffect, effect_id) == 4, + OffsetOf_SetEffect_effect_id_not_4); + +struct GetParamCount { + typedef GetParamCount ValueType; + static const CommandId kCmdId = kGetParamCount; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _effect_id, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + effect_id = _effect_id; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId effect_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(effect_id, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId effect_id; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(GetParamCount) == 20, Sizeof_GetParamCount_is_not_20); +COMPILE_ASSERT(offsetof(GetParamCount, header) == 0, + OffsetOf_GetParamCount_header_not_0); +COMPILE_ASSERT(offsetof(GetParamCount, effect_id) == 4, + OffsetOf_GetParamCount_effect_id_not_4); +COMPILE_ASSERT(offsetof(GetParamCount, size) == 8, + OffsetOf_GetParamCount_size_not_8); +COMPILE_ASSERT(offsetof(GetParamCount, shared_memory) == 12, + OffsetOf_GetParamCount_shared_memory_not_12); + +struct CreateParam { + typedef CreateParam ValueType; + static const CommandId kCmdId = kCreateParam; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _param_id, ResourceId _effect_id, uint32 _index) { + SetHeader(); + param_id = _param_id; + effect_id = _effect_id; + index = _index; + } + + static void* Set(void* cmd, + ResourceId param_id, ResourceId effect_id, uint32 index) { + static_cast<ValueType*>(cmd)->Init(param_id, effect_id, index); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId param_id; + ResourceId effect_id; + uint32 index; +}; + +COMPILE_ASSERT(sizeof(CreateParam) == 16, Sizeof_CreateParam_is_not_16); +COMPILE_ASSERT(offsetof(CreateParam, header) == 0, + OffsetOf_CreateParam_header_not_0); +COMPILE_ASSERT(offsetof(CreateParam, param_id) == 4, + OffsetOf_CreateParam_param_id_not_4); +COMPILE_ASSERT(offsetof(CreateParam, effect_id) == 8, + OffsetOf_CreateParam_effect_id_not_8); +COMPILE_ASSERT(offsetof(CreateParam, index) == 12, + OffsetOf_CreateParam_index_not_12); + +struct CreateParamByName { + typedef CreateParamByName ValueType; + static const CommandId kCmdId = kCreateParamByName; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _param_id, ResourceId _effect_id, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + param_id = _param_id; + effect_id = _effect_id; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId param_id, ResourceId effect_id, + uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(param_id, effect_id, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId param_id; + ResourceId effect_id; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(CreateParamByName) == 24, + Sizeof_CreateParamByName_is_not_24); +COMPILE_ASSERT(offsetof(CreateParamByName, header) == 0, + OffsetOf_CreateParamByName_header_not_0); +COMPILE_ASSERT(offsetof(CreateParamByName, param_id) == 4, + OffsetOf_CreateParamByName_param_id_not_4); +COMPILE_ASSERT(offsetof(CreateParamByName, effect_id) == 8, + OffsetOf_CreateParamByName_effect_id_not_8); +COMPILE_ASSERT(offsetof(CreateParamByName, size) == 12, + OffsetOf_CreateParamByName_size_not_12); +COMPILE_ASSERT(offsetof(CreateParamByName, shared_memory) == 16, + OffsetOf_CreateParamByName_shared_memory_not_16); + +struct CreateParamByNameImmediate { + typedef CreateParamByNameImmediate ValueType; + static const CommandId kCmdId = kCreateParamByNameImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + void SetHeader(uint32 size) { + header.SetCmdBySize<ValueType>(size); + } + + void Init(ResourceId _param_id, ResourceId _effect_id, uint32 _size, + const void* data) { + SetHeader(_size); + param_id = _param_id; + effect_id = _effect_id; + size = _size; + memcpy(ImmediateDataAddress(this), data, _size); + } + + static void* Set(void* cmd, ResourceId param_id, ResourceId effect_id, + uint32 size, const void* data) { + static_cast<ValueType*>(cmd)->Init(param_id, effect_id, size, data); + return NextImmediateCmdAddress<ValueType>(cmd, size); + } + + CommandHeader header; + ResourceId param_id; + ResourceId effect_id; + uint32 size; +}; + +COMPILE_ASSERT(sizeof(CreateParamByNameImmediate) == 16, + Sizeof_CreateParamByNameImmediate_is_not_16); +COMPILE_ASSERT(offsetof(CreateParamByNameImmediate, header) == 0, + OffsetOf_CreateParamByNameImmediate_header_not_0); +COMPILE_ASSERT(offsetof(CreateParamByNameImmediate, param_id) == 4, + OffsetOf_CreateParamByNameImmediate_param_id_not_4); +COMPILE_ASSERT(offsetof(CreateParamByNameImmediate, effect_id) == 8, + OffsetOf_CreateParamByNameImmediate_effect_id_not_8); +COMPILE_ASSERT(offsetof(CreateParamByNameImmediate, size) == 12, + OffsetOf_CreateParamByNameImmediate_size_not_12); + +struct DestroyParam { + typedef DestroyParam ValueType; + static const CommandId kCmdId = kDestroyParam; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _param_id) { + SetHeader(); + param_id = _param_id; + } + + static void* Set(void* cmd, ResourceId param_id) { + static_cast<ValueType*>(cmd)->Init(param_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId param_id; +}; + +COMPILE_ASSERT(sizeof(DestroyParam) == 8, Sizeof_DestroyParam_is_not_8); +COMPILE_ASSERT(offsetof(DestroyParam, header) == 0, + OffsetOf_DestroyParam_header_not_0); +COMPILE_ASSERT(offsetof(DestroyParam, param_id) == 4, + OffsetOf_DestroyParam_param_id_not_4); + +struct SetParamData { + typedef SetParamData ValueType; + static const CommandId kCmdId = kSetParamData; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _param_id, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + param_id = _param_id; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId param_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(param_id, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId param_id; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(SetParamData) == 20, Sizeof_SetParamData_is_not_20); +COMPILE_ASSERT(offsetof(SetParamData, header) == 0, + OffsetOf_SetParamData_header_not_0); +COMPILE_ASSERT(offsetof(SetParamData, param_id) == 4, + OffsetOf_SetParamData_param_id_not_4); +COMPILE_ASSERT(offsetof(SetParamData, size) == 8, + OffsetOf_SetParamData_size_not_8); +COMPILE_ASSERT(offsetof(SetParamData, shared_memory) == 12, + OffsetOf_SetParamData_shared_memory_not_12); + +struct SetParamDataImmediate { + typedef SetParamDataImmediate ValueType; + static const CommandId kCmdId = kSetParamDataImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + void SetHeader(uint32 size) { + header.SetCmdBySize<ValueType>(size); + } + + void Init(ResourceId _param_id, uint32 _size, const void* data) { + SetHeader(_size); + param_id = _param_id; + size = _size; + memcpy(ImmediateDataAddress(this), data, _size); + } + + static void* Set(void* cmd, ResourceId param_id, + uint32 size, const void* data) { + static_cast<ValueType*>(cmd)->Init(param_id, size, data); + return NextImmediateCmdAddress<ValueType>(cmd, size); + } + + CommandHeader header; + ResourceId param_id; + uint32 size; +}; + +COMPILE_ASSERT(sizeof(SetParamDataImmediate) == 12, + Sizeof_SetParamDataImmediate_is_not_12); +COMPILE_ASSERT(offsetof(SetParamDataImmediate, header) == 0, + OffsetOf_SetParamDataImmediate_header_not_0); +COMPILE_ASSERT(offsetof(SetParamDataImmediate, param_id) == 4, + OffsetOf_SetParamDataImmediate_param_id_not_4); +COMPILE_ASSERT(offsetof(SetParamDataImmediate, size) == 8, + OffsetOf_SetParamDataImmediate_size_not_8); + +struct GetParamDesc { + typedef GetParamDesc ValueType; + static const CommandId kCmdId = kGetParamDesc; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _param_id, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + param_id = _param_id; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId param_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(param_id, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId param_id; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(GetParamDesc) == 20, Sizeof_GetParamDesc_is_not_20); +COMPILE_ASSERT(offsetof(GetParamDesc, header) == 0, + OffsetOf_GetParamDesc_header_not_0); +COMPILE_ASSERT(offsetof(GetParamDesc, param_id) == 4, + OffsetOf_GetParamDesc_id_not_4); +COMPILE_ASSERT(offsetof(GetParamDesc, size) == 8, + OffsetOf_GetParamDesc_size_not_8); +COMPILE_ASSERT(offsetof(GetParamDesc, shared_memory) == 12, + OffsetOf_GetParamDesc_shared_memory_not_12); + +struct GetStreamCount { + typedef GetStreamCount ValueType; + static const CommandId kCmdId = kGetStreamCount; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _effect_id, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + effect_id = _effect_id; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId effect_id, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(effect_id, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId effect_id; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(GetStreamCount) == 20, + Sizeof_GetStreamCount_is_not_20); +COMPILE_ASSERT(offsetof(GetStreamCount, header) == 0, + OffsetOf_GetStreamCount_header_not_0); +COMPILE_ASSERT(offsetof(GetStreamCount, effect_id) == 4, + OffsetOf_GetStreamCount_effect_id_not_4); +COMPILE_ASSERT(offsetof(GetStreamCount, size) == 8, + OffsetOf_GetStreamCount_size_not_8); +COMPILE_ASSERT(offsetof(GetStreamCount, shared_memory) == 12, + OffsetOf_GetStreamCount_shared_memory_not_12); + +struct GetStreamDesc { + typedef GetStreamDesc ValueType; + static const CommandId kCmdId = kGetStreamDesc; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _effect_id, uint32 _index, uint32 _size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + SetHeader(); + effect_id = _effect_id; + index = _index; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set(void* cmd, ResourceId effect_id, uint32 index, uint32 size, + uint32 shared_memory_id, uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init(effect_id, index, size, + shared_memory_id, shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId effect_id; + uint32 index; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(GetStreamDesc) == 24, Sizeof_GetStreamDesc_is_not_24); +COMPILE_ASSERT(offsetof(GetStreamDesc, header) == 0, + OffsetOf_GetStreamDesc_header_not_0); +COMPILE_ASSERT(offsetof(GetStreamDesc, effect_id) ==4 , + OffsetOf_GetStreamDesc_effect_id_not_4); +COMPILE_ASSERT(offsetof(GetStreamDesc, index) == 8, + OffsetOf_GetStreamDesc_index_not_8); +COMPILE_ASSERT(offsetof(GetStreamDesc, size) == 12, + OffsetOf_GetStreamDesc_size_not_12); +COMPILE_ASSERT(offsetof(GetStreamDesc, shared_memory) == 16, + OffsetOf_GetStreamDesc_shared_memory_not_16); + +struct DestroyTexture { + typedef DestroyTexture ValueType; + static const CommandId kCmdId = kDestroyTexture; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _texture_id) { + SetHeader(); + texture_id = _texture_id; + } + + static void* Set(void* cmd, ResourceId texture_id) { + static_cast<ValueType*>(cmd)->Init(texture_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId texture_id; +}; + +COMPILE_ASSERT(sizeof(DestroyTexture) == 8, Sizeof_DestroyTexture_is_not_8); +COMPILE_ASSERT(offsetof(DestroyTexture, header) == 0, + OffsetOf_DestroyTexture_header_not_0); +COMPILE_ASSERT(offsetof(DestroyTexture, texture_id) == 4, + OffsetOf_DestroyTexture_texture_id_not_4); + +struct CreateTexture2d { + typedef CreateTexture2d ValueType; + static const CommandId kCmdId = kCreateTexture2d; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 1 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + // argument 2 + typedef BitField<0, 4> Levels; + typedef BitField<4, 4> Unused; + typedef BitField<8, 8> Format; + typedef BitField<16, 16> Flags; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _texture_id, + uint32 _width, uint32 _height, uint32 _levels, + texture::Format _format, + bool _enable_render_surfaces) { + SetHeader(); + texture_id = _texture_id; + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + levels_format_flags = + Levels::MakeValue(_levels) | + Format::MakeValue(_format) | + Flags::MakeValue(_enable_render_surfaces ? 1 : 0); + } + + static void* Set(void* cmd, ResourceId texture_id, + uint32 width, uint32 height, uint32 levels, + texture::Format format, + bool enable_render_surfaces) { + static_cast<ValueType*>(cmd)->Init(texture_id, + width, height, levels, format, + enable_render_surfaces); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId texture_id; + uint32 width_height; + uint32 levels_format_flags; +}; + +COMPILE_ASSERT(sizeof(CreateTexture2d) == 16, + Sizeof_CreateTexture2d_is_not_16); +COMPILE_ASSERT(offsetof(CreateTexture2d, header) == 0, + OffsetOf_CreateTexture2d_header_not_0); +COMPILE_ASSERT(offsetof(CreateTexture2d, texture_id) == 4, + OffsetOf_CreateTexture2d_texture_id_not_4); +COMPILE_ASSERT(offsetof(CreateTexture2d, width_height) == 8, + OffsetOf_CreateTexture2d_width_height_not_8); +COMPILE_ASSERT(offsetof(CreateTexture2d, levels_format_flags) == 12, + OffsetOf_CreateTexture2d_levels_format_flags_not_12); + +struct CreateTexture3d { + typedef CreateTexture3d ValueType; + static const CommandId kCmdId = kCreateTexture3d; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 1 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + // argument 2 + typedef BitField<0, 16> Depth; + typedef BitField<16, 16> Unused1; + // argument 3 + typedef BitField<0, 4> Levels; + typedef BitField<4, 4> Unused2; + typedef BitField<8, 8> Format; + typedef BitField<16, 16> Flags; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _texture_id, + uint32 _width, uint32 _height, uint32 _depth, + uint32 _levels, texture::Format _format, + bool _enable_render_surfaces) { + SetHeader(); + texture_id = _texture_id; + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + depth_unused = Depth::MakeValue(_depth); + levels_format_flags = + Levels::MakeValue(_levels) | + Format::MakeValue(_format) | + Flags::MakeValue(_enable_render_surfaces ? 1 : 0); + } + + static void* Set(void* cmd, ResourceId texture_id, + uint32 width, uint32 height, uint32 depth, + uint32 levels, texture::Format format, + bool enable_render_surfaces) { + static_cast<ValueType*>(cmd)->Init(texture_id, + width, height, depth, + levels, format, + enable_render_surfaces); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId texture_id; + uint32 width_height; + uint32 depth_unused; + uint32 levels_format_flags; +}; + +COMPILE_ASSERT(sizeof(CreateTexture3d) == 20, + Sizeof_CreateTexture3d_is_not_20); +COMPILE_ASSERT(offsetof(CreateTexture3d, header) == 0, + OffsetOf_CreateTexture3d_header_not_0); +COMPILE_ASSERT(offsetof(CreateTexture3d, texture_id) == 4, + OffsetOf_CreateTexture3d_texture_id_not_4); +COMPILE_ASSERT(offsetof(CreateTexture3d, width_height) == 8, + OffsetOf_CreateTexture3d_width_height_not_8); +COMPILE_ASSERT(offsetof(CreateTexture3d, depth_unused) == 12, + OffsetOf_CreateTexture3d_depth_unused_not_12); +COMPILE_ASSERT(offsetof(CreateTexture3d, levels_format_flags) == 16, + OffsetOf_CreateTexture3d_levels_format_flags_not_16); + +struct CreateTextureCube { + typedef CreateTextureCube ValueType; + static const CommandId kCmdId = kCreateTextureCube; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 1 + typedef BitField<0, 16> Side; + typedef BitField<16, 16> Unused1; + // argument 2 + typedef BitField<0, 4> Levels; + typedef BitField<4, 4> Unused2; + typedef BitField<8, 8> Format; + typedef BitField<16, 16> Flags; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _texture_id, + uint32 _edge_length, uint32 _levels, texture::Format _format, + bool _enable_render_surfaces) { + SetHeader(); + texture_id = _texture_id; + edge_length = _edge_length; + levels_format_flags = + Levels::MakeValue(_levels) | + Format::MakeValue(_format) | + Flags::MakeValue(_enable_render_surfaces ? 1 : 0); + } + + static void* Set(void* cmd, ResourceId texture_id, + uint32 edge_length, uint32 levels, texture::Format format, + bool enable_render_surfaces) { + static_cast<ValueType*>(cmd)->Init(texture_id, + edge_length, levels, format, + enable_render_surfaces); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId texture_id; + uint32 edge_length; + uint32 levels_format_flags; +}; + +COMPILE_ASSERT(sizeof(CreateTextureCube) == 16, + Sizeof_CreateTextureCube_is_not_16); +COMPILE_ASSERT(offsetof(CreateTextureCube, header) == 0, + OffsetOf_CreateTextureCube_header_not_0); +COMPILE_ASSERT(offsetof(CreateTextureCube, texture_id) == 4, + OffsetOf_CreateTextureCube_texture_id_not_4); +COMPILE_ASSERT(offsetof(CreateTextureCube, edge_length) == 8, + OffsetOf_CreateTextureCube_edge_length_not_8); +COMPILE_ASSERT(offsetof(CreateTextureCube, levels_format_flags) == 12, + OffsetOf_CreateTextureCube_levels_format_flags_not_12); + +struct SetTextureData { + typedef SetTextureData ValueType; + static const CommandId kCmdId = kSetTextureData; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 1 + typedef BitField<0, 16> X; + typedef BitField<16, 16> Y; + // argument 2 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + // argument 3 + typedef BitField<0, 16> Z; + typedef BitField<16, 16> Depth; + // argument 4 + typedef BitField<0, 4> Level; + typedef BitField<4, 3> Face; + typedef BitField<7, 25> Unused; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init( + ResourceId _texture_id, + uint32 _x, + uint32 _y, + uint32 _z, + uint32 _width, + uint32 _height, + uint32 _depth, + uint32 _level, + texture::Face _face, + uint32 _row_pitch, + uint32 _slice_pitch, + uint32 _size, + uint32 shared_memory_id, + uint32 shared_memory_offset) { + SetHeader(); + texture_id = _texture_id; + x_y = X::MakeValue(_x) | Y::MakeValue(_y); + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + z_depth = Z::MakeValue(_z) | Depth::MakeValue(_depth); + level_face = Level::MakeValue(_level) | Face::MakeValue(_face); + row_pitch = _row_pitch; + slice_pitch = _slice_pitch; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set( + void* cmd, + ResourceId texture_id, + uint32 x, + uint32 y, + uint32 z, + uint32 width, + uint32 height, + uint32 depth, + uint32 level, + texture::Face face, + uint32 row_pitch, + uint32 slice_pitch, + uint32 size, + uint32 shared_memory_id, + uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init( + texture_id, + x, + y, + z, + width, + height, + depth, + level, + face, + row_pitch, + slice_pitch, + size, + shared_memory_id, + shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId texture_id; + uint32 x_y; + uint32 width_height; + uint32 z_depth; + uint32 level_face; + uint32 row_pitch; + uint32 slice_pitch; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(SetTextureData) == 44, + Sizeof_SetTextureData_is_not_44); +COMPILE_ASSERT(offsetof(SetTextureData, header) == 0, + OffsetOf_SetTextureData_header_not_0); +COMPILE_ASSERT(offsetof(SetTextureData, texture_id) == 4, + OffsetOf_SetTextureData_texture_id_not_4); +COMPILE_ASSERT(offsetof(SetTextureData, x_y) == 8, + OffsetOf_SetTextureData_x_y_not_8); +COMPILE_ASSERT(offsetof(SetTextureData, width_height) == 12, + OffsetOf_SetTextureData_width_height_not_12); +COMPILE_ASSERT(offsetof(SetTextureData, z_depth) == 16, + OffsetOf_SetTextureData_z_depth_not_16); +COMPILE_ASSERT(offsetof(SetTextureData, level_face) == 20, + OffsetOf_SetTextureData_level_face_not_20); +COMPILE_ASSERT(offsetof(SetTextureData, row_pitch) == 24, + OffsetOf_SetTextureData_row_pitch_not_24); +COMPILE_ASSERT(offsetof(SetTextureData, slice_pitch) == 28, + OffsetOf_SetTextureData_slice_pitch_not_28); +COMPILE_ASSERT(offsetof(SetTextureData, size) == 32, + OffsetOf_SetTextureData_size_not_32); +COMPILE_ASSERT(offsetof(SetTextureData, shared_memory) == 36, + OffsetOf_SetTextureData_shared_memory_not_36); + +struct SetTextureDataImmediate { + typedef SetTextureDataImmediate ValueType; + static const CommandId kCmdId = kSetTextureDataImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + // argument 1 + typedef BitField<0, 16> X; + typedef BitField<16, 16> Y; + // argument 2 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + // argument 3 + typedef BitField<0, 16> Z; + typedef BitField<16, 16> Depth; + // argument 4 + typedef BitField<0, 4> Level; + typedef BitField<4, 3> Face; + typedef BitField<7, 25> Unused; + + void SetHeader(uint32 size) { + header.SetCmdBySize<ValueType>(size); + } + + void Init( + ResourceId _texture_id, + uint32 _x, + uint32 _y, + uint32 _z, + uint32 _width, + uint32 _height, + uint32 _depth, + uint32 _level, + texture::Face _face, + uint32 _row_pitch, + uint32 _slice_pitch, + uint32 _size, + const void* data) { + SetHeader(_size); + texture_id = _texture_id; + x_y = X::MakeValue(_x) | Y::MakeValue(_y); + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + z_depth = Z::MakeValue(_z) | Depth::MakeValue(_depth); + level_face = Level::MakeValue(_level) | Face::MakeValue(_face); + row_pitch = _row_pitch; + slice_pitch = _slice_pitch; + size = _size; + memcpy(ImmediateDataAddress(this), data, _size); + } + + static void* Set( + void* cmd, + ResourceId texture_id, + uint32 x, + uint32 y, + uint32 z, + uint32 width, + uint32 height, + uint32 depth, + uint32 level, + texture::Face face, + uint32 row_pitch, + uint32 slice_pitch, + uint32 size, + const void* data) { + static_cast<ValueType*>(cmd)->Init( + texture_id, + x, + y, + z, + width, + height, + depth, + level, + face, + row_pitch, + slice_pitch, + size, + data); + return NextImmediateCmdAddress<ValueType>(cmd, size); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId texture_id; + uint32 x_y; + uint32 width_height; + uint32 z_depth; + uint32 level_face; + uint32 row_pitch; + uint32 slice_pitch; + uint32 size; +}; + +COMPILE_ASSERT(sizeof(SetTextureDataImmediate) == 36, + Sizeof_SetTextureDataImmediate_is_not_36); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, header) == 0, + OffsetOf_SetTextureDataImmediate_header_not_0); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, texture_id) == 4, + OffsetOf_SetTextureDataImmediate_texture_id_not_4); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, x_y) == 8, + OffsetOf_SetTextureDataImmediate_x_y_not_8); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, width_height) == 12, + OffsetOf_SetTextureDataImmediate_width_height_not_12); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, z_depth) == 16, + OffsetOf_SetTextureDataImmediate_z_depth_not_16); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, level_face) == 20, + OffsetOf_SetTextureDataImmediate_level_face_not_20); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, row_pitch) == 24, + OffsetOf_SetTextureDataImmediate_row_pitch_not_24); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, slice_pitch) == 28, + OffsetOf_SetTextureDataImmediate_slice_pitch_not_28); +COMPILE_ASSERT(offsetof(SetTextureDataImmediate, size) == 32, + OffsetOf_SetTextureDataImmediate_size_not_32); + +struct GetTextureData { + typedef GetTextureData ValueType; + static const CommandId kCmdId = kGetTextureData; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 1 + typedef BitField<0, 16> X; + typedef BitField<16, 16> Y; + // argument 2 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + // argument 3 + typedef BitField<0, 16> Z; + typedef BitField<16, 16> Depth; + // argument 4 + typedef BitField<0, 4> Level; + typedef BitField<4, 3> Face; + typedef BitField<7, 25> Unused; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init( + ResourceId _texture_id, + uint32 _x, + uint32 _y, + uint32 _z, + uint32 _width, + uint32 _height, + uint32 _depth, + uint32 _level, + texture::Face _face, + uint32 _row_pitch, + uint32 _slice_pitch, + uint32 _size, + uint32 shared_memory_id, + uint32 shared_memory_offset) { + SetHeader(); + texture_id = _texture_id; + x_y = X::MakeValue(_x) | Y::MakeValue(_y); + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + z_depth = Z::MakeValue(_z) | Depth::MakeValue(_depth); + level_face = Level::MakeValue(_level) | Face::MakeValue(_face); + row_pitch = _row_pitch; + slice_pitch = _slice_pitch; + size = _size; + shared_memory.Init(shared_memory_id, shared_memory_offset); + } + + static void* Set( + void* cmd, + ResourceId texture_id, + uint32 x, + uint32 y, + uint32 z, + uint32 width, + uint32 height, + uint32 depth, + uint32 level, + texture::Face face, + uint32 row_pitch, + uint32 slice_pitch, + uint32 size, + uint32 shared_memory_id, + uint32 shared_memory_offset) { + static_cast<ValueType*>(cmd)->Init( + texture_id, + x, + y, + z, + width, + height, + depth, + level, + face, + row_pitch, + slice_pitch, + size, + shared_memory_id, + shared_memory_offset); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId texture_id; + uint32 x_y; + uint32 width_height; + uint32 z_depth; + uint32 level_face; + uint32 row_pitch; + uint32 slice_pitch; + uint32 size; + SharedMemory shared_memory; +}; + +COMPILE_ASSERT(sizeof(GetTextureData) == 44, + Sizeof_GetTextureData_is_not_44); +COMPILE_ASSERT(offsetof(GetTextureData, header) == 0, + OffsetOf_GetTextureData_header_not_0); +COMPILE_ASSERT(offsetof(GetTextureData, texture_id) == 4, + OffsetOf_GetTextureData_texture_id_not_4); +COMPILE_ASSERT(offsetof(GetTextureData, x_y) == 8, + OffsetOf_GetTextureData_x_y_not_8); +COMPILE_ASSERT(offsetof(GetTextureData, width_height) == 12, + OffsetOf_GetTextureData_width_height_not_12); +COMPILE_ASSERT(offsetof(GetTextureData, z_depth) == 16, + OffsetOf_GetTextureData_z_depth_not_16); +COMPILE_ASSERT(offsetof(GetTextureData, level_face) == 20, + OffsetOf_GetTextureData_level_face_not_20); +COMPILE_ASSERT(offsetof(GetTextureData, row_pitch) == 24, + OffsetOf_GetTextureData_row_pitch_not_24); +COMPILE_ASSERT(offsetof(GetTextureData, slice_pitch) == 28, + OffsetOf_GetTextureData_slice_pitch_not_28); +COMPILE_ASSERT(offsetof(GetTextureData, size) == 32, + OffsetOf_GetTextureData_size_not_32); +COMPILE_ASSERT(offsetof(GetTextureData, shared_memory) == 36, + OffsetOf_GetTextureData_shared_memory_not_36); + +struct CreateSampler { + typedef CreateSampler ValueType; + static const CommandId kCmdId = kCreateSampler; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _sampler_id) { + SetHeader(); + sampler_id = _sampler_id; + } + + static void* Set(void* cmd, ResourceId sampler_id) { + static_cast<ValueType*>(cmd)->Init(sampler_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId sampler_id; +}; + +COMPILE_ASSERT(sizeof(CreateSampler) == 8, Sizeof_CreateSampler_is_not_8); +COMPILE_ASSERT(offsetof(CreateSampler, header) == 0, + OffsetOf_CreateSampler_header_not_0); +COMPILE_ASSERT(offsetof(CreateSampler, sampler_id) == 4, + OffsetOf_CreateSampler_sampler_id_not_4); + +struct DestroySampler { + typedef DestroySampler ValueType; + static const CommandId kCmdId = kDestroySampler; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _sampler_id) { + SetHeader(); + sampler_id = _sampler_id; + } + + static void* Set(void* cmd, ResourceId sampler_id) { + static_cast<ValueType*>(cmd)->Init(sampler_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId sampler_id; +}; + +COMPILE_ASSERT(sizeof(DestroySampler) == 8, Sizeof_DestroySampler_is_not_8); +COMPILE_ASSERT(offsetof(DestroySampler, header) == 0, + OffsetOf_DestroySampler_header_not_0); +COMPILE_ASSERT(offsetof(DestroySampler, sampler_id) == 4, + OffsetOf_DestroySampler_sampler_id_not_4); + +struct SetSamplerStates { + typedef SetSamplerStates ValueType; + static const CommandId kCmdId = kSetSamplerStates; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 2 + typedef BitField<0, 3> AddressingU; + typedef BitField<3, 3> AddressingV; + typedef BitField<6, 3> AddressingW; + typedef BitField<9, 3> MagFilter; + typedef BitField<12, 3> MinFilter; + typedef BitField<15, 3> MipFilter; + typedef BitField<18, 6> Unused; + typedef BitField<24, 8> MaxAnisotropy; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init( + ResourceId _sampler_id, + sampler::AddressingMode _address_u_value, + sampler::AddressingMode _address_v_value, + sampler::AddressingMode _address_w_value, + sampler::FilteringMode _mag_filter_value, + sampler::FilteringMode _min_filter_value, + sampler::FilteringMode _mip_filter_value, + uint8 _max_anisotropy) { + SetHeader(); + sampler_id = _sampler_id; + sampler_states = + AddressingU::MakeValue(_address_u_value) | + AddressingV::MakeValue(_address_v_value) | + AddressingW::MakeValue(_address_w_value) | + MagFilter::MakeValue(_mag_filter_value) | + MinFilter::MakeValue(_min_filter_value) | + MipFilter::MakeValue(_mip_filter_value) | + MaxAnisotropy::MakeValue(_max_anisotropy); + } + + static void* Set(void* cmd, + ResourceId sampler_id, + sampler::AddressingMode address_u_value, + sampler::AddressingMode address_v_value, + sampler::AddressingMode address_w_value, + sampler::FilteringMode mag_filter_value, + sampler::FilteringMode min_filter_value, + sampler::FilteringMode mip_filter_value, + uint8 max_anisotropy) { + static_cast<ValueType*>(cmd)->Init( + sampler_id, + address_u_value, + address_v_value, + address_w_value, + mag_filter_value, + min_filter_value, + mip_filter_value, + max_anisotropy); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId sampler_id; + uint32 sampler_states; +}; + +COMPILE_ASSERT(sizeof(SetSamplerStates) == 12, + Sizeof_SetSamplerStates_is_not_12); +COMPILE_ASSERT(offsetof(SetSamplerStates, header) == 0, + OffsetOf_SetSamplerStates_header_not_0); +COMPILE_ASSERT(offsetof(SetSamplerStates, sampler_id) == 4, + OffsetOf_SetSamplerStates_sampler_id_not_4); +COMPILE_ASSERT(offsetof(SetSamplerStates, sampler_states) == 8, + OffsetOf_SetSamplerStates_sampler_states_not_8); + +struct SetSamplerBorderColor { + typedef SetSamplerBorderColor ValueType; + static const CommandId kCmdId = kSetSamplerBorderColor; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _sampler_id, + float _red, float _green, float _blue, float _alpha) { + SetHeader(); + sampler_id = _sampler_id; + red = _red; + green = _green; + blue = _blue; + alpha = _alpha; + } + + static void* Set(void* cmd, ResourceId sampler_id, + float red, float green, float blue, float alpha) { + static_cast<ValueType*>(cmd)->Init(sampler_id, red, green, blue, alpha); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId sampler_id; + float red; + float blue; + float green; + float alpha; +}; + +COMPILE_ASSERT(sizeof(SetSamplerBorderColor) == 24, + Sizeof_SetSamplerBorderColor_is_not_24); +COMPILE_ASSERT(offsetof(SetSamplerBorderColor, header) == 0, + OffsetOf_SetSamplerBorderColor_header_not_0); +COMPILE_ASSERT(offsetof(SetSamplerBorderColor, sampler_id) == 4, + OffsetOf_SetSamplerBorderColor_sampler_id_not_4); +COMPILE_ASSERT(offsetof(SetSamplerBorderColor, red) == 8, + OffsetOf_SetSamplerBorderColor_red_not_8); +COMPILE_ASSERT(offsetof(SetSamplerBorderColor, blue) == 12, + OffsetOf_SetSamplerBorderColor_blue_not_12); +COMPILE_ASSERT(offsetof(SetSamplerBorderColor, green) == 16, + OffsetOf_SetSamplerBorderColor_green_not_16); +COMPILE_ASSERT(offsetof(SetSamplerBorderColor, alpha) == 20, + OffsetOf_SetSamplerBorderColor_alpha_not_20); + +struct SetSamplerTexture { + typedef SetSamplerTexture ValueType; + static const CommandId kCmdId = kSetSamplerTexture; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _sampler_id, ResourceId _texture_id) { + SetHeader(); + sampler_id = _sampler_id; + texture_id = _texture_id; + } + + static void* Set(void* cmd, ResourceId sampler_id, ResourceId texture_id) { + static_cast<ValueType*>(cmd)->Init(sampler_id, texture_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId sampler_id; + ResourceId texture_id; +}; + +COMPILE_ASSERT(sizeof(SetSamplerTexture) == 12, + Sizeof_SetSamplerTexture_is_not_12); +COMPILE_ASSERT(offsetof(SetSamplerTexture, header) == 0, + OffsetOf_SetSamplerTexture_header_not_0); +COMPILE_ASSERT(offsetof(SetSamplerTexture, sampler_id) == 4, + OffsetOf_SetSamplerTexture_sampler_id_not_4); +COMPILE_ASSERT(offsetof(SetSamplerTexture, texture_id) == 8, + OffsetOf_SetSamplerTexture_texture_id_not_8); + +struct SetScissor { + typedef SetScissor ValueType; + static const CommandId kCmdId = kSetScissor; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 15> X; + typedef BitField<15, 1> Unused; + typedef BitField<16, 15> Y; + typedef BitField<31, 1> Enable; + // argument 1 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(uint32 _x, + uint32 _y, + uint32 _width, + uint32 _height, + bool _enable) { + SetHeader(); + x_y_enable = + X::MakeValue(_x) | + Y::MakeValue(_y) | + Enable::MakeValue(_enable ? 1 : 0); + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + } + + static void* Set( + void* cmd, + uint32 x, + uint32 y, + uint32 width, + uint32 height, + bool enable) { + static_cast<ValueType*>(cmd)->Init( + x, + y, + width, + height, + enable); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + uint32 x_y_enable; + uint32 width_height; +}; + +COMPILE_ASSERT(sizeof(SetScissor) == 12, Sizeof_SetScissor_is_not_12); +COMPILE_ASSERT(offsetof(SetScissor, header) == 0, + OffsetOf_SetScissor_header_not_0); +COMPILE_ASSERT(offsetof(SetScissor, x_y_enable) == 4, + OffsetOf_SetScissor_x_y_enable_not_4); +COMPILE_ASSERT(offsetof(SetScissor, width_height) == 8, + OffsetOf_SetScissor_width_height_not_8); + +struct SetPolygonOffset { + typedef SetPolygonOffset ValueType; + static const CommandId kCmdId = kSetPolygonOffset; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(float _slope_factor, float _units) { + SetHeader(); + slope_factor = _slope_factor; + units = _units; + } + + static void* Set(void* cmd, float slope_factor, float units) { + static_cast<ValueType*>(cmd)->Init(slope_factor, units); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + float slope_factor; + float units; +}; + +COMPILE_ASSERT(sizeof(SetPolygonOffset) == 12, + Sizeof_SetPolygonOffset_is_not_12); +COMPILE_ASSERT(offsetof(SetPolygonOffset, header) == 0, + OffsetOf_SetPolygonOffset_header_not_0); +COMPILE_ASSERT(offsetof(SetPolygonOffset, slope_factor) == 4, + OffsetOf_SetPolygonOffset_slope_factor_not_4); +COMPILE_ASSERT(offsetof(SetPolygonOffset, units) == 8, + OffsetOf_SetPolygonOffset_units_not_8); + +struct SetPointLineRaster { + typedef SetPointLineRaster ValueType; + static const CommandId kCmdId = kSetPointLineRaster; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 1> LineSmoothEnable; + typedef BitField<1, 1> PointSpriteEnable; + typedef BitField<2, 30> Unused; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(bool _line_smooth_enable, bool _point_sprite_enable, + float _point_size) { + SetHeader(); + enables = + LineSmoothEnable::MakeValue(_line_smooth_enable ? 1 : 0) | + PointSpriteEnable::MakeValue(_point_sprite_enable ? 1 : 0); + point_size = _point_size; + } + + static void* Set(void* cmd, bool line_smooth_enable, bool point_sprite_enable, + float point_size) { + static_cast<ValueType*>(cmd)->Init(line_smooth_enable, point_sprite_enable, + point_size); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + uint32 enables; + float point_size; +}; + +COMPILE_ASSERT(sizeof(SetPointLineRaster) == 12, + Sizeof_SetPointLineRaster_is_not_12); +COMPILE_ASSERT(offsetof(SetPointLineRaster, header) == 0, + OffsetOf_SetPointLineRaster_header_not_0); +COMPILE_ASSERT(offsetof(SetPointLineRaster, enables) == 4, + OffsetOf_SetPointLineRaster_enables_not_4); +COMPILE_ASSERT(offsetof(SetPointLineRaster, point_size) == 8, + OffsetOf_SetPointLineRaster_point_size_not_8); + +struct SetPolygonRaster { + typedef SetPolygonRaster ValueType; + static const CommandId kCmdId = kSetPolygonRaster; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 2> FillMode; + typedef BitField<2, 2> CullMode; + typedef BitField<4, 28> Unused; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(PolygonMode _fill_mode, FaceCullMode _cull_mode) { + SetHeader(); + fill_cull = FillMode::MakeValue(_fill_mode) | + CullMode::MakeValue(_cull_mode); + } + + static void* Set(void* cmd, PolygonMode fill_mode, FaceCullMode cull_mode) { + static_cast<ValueType*>(cmd)->Init(fill_mode, cull_mode); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + uint32 fill_cull; +}; + +COMPILE_ASSERT(sizeof(SetPolygonRaster) == 8, + Sizeof_SetPolygonRaster_is_not_8); +COMPILE_ASSERT(offsetof(SetPolygonRaster, header) == 0, + OffsetOf_SetPolygonRaster_header_not_0); +COMPILE_ASSERT(offsetof(SetPolygonRaster, fill_cull) == 4, + OffsetOf_SetPolygonRaster_fill_cull_not_4); + +struct SetAlphaTest { + typedef SetAlphaTest ValueType; + static const CommandId kCmdId = kSetAlphaTest; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 3> Func; + typedef BitField<3, 28> Unused; + typedef BitField<31, 1> Enable; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(Comparison _func, bool _enable, float _value) { + SetHeader(); + func_enable = Func::MakeValue(_func) | Enable::MakeValue(_enable ? 1 : 0); + value = _value; + } + + static void* Set(void* cmd, Comparison func, bool enable, float value) { + static_cast<ValueType*>(cmd)->Init(func, enable, value); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + uint32 func_enable; + float value; +}; + +COMPILE_ASSERT(sizeof(SetAlphaTest) == 12, Sizeof_SetAlphaTest_is_not_12); +COMPILE_ASSERT(offsetof(SetAlphaTest, header) == 0, + OffsetOf_SetAlphaTest_header_not_0); +COMPILE_ASSERT(offsetof(SetAlphaTest, func_enable) == 4, + OffsetOf_SetAlphaTest_func_enable_not_4); +COMPILE_ASSERT(offsetof(SetAlphaTest, value) == 8, + OffsetOf_SetAlphaTest_value_not_8); + +struct SetDepthTest { + typedef SetDepthTest ValueType; + static const CommandId kCmdId = kSetDepthTest; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 3> Func; + typedef BitField<3, 27> Unused; + typedef BitField<30, 1> WriteEnable; + typedef BitField<31, 1> Enable; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(Comparison _func, bool _write_enable, bool _enable) { + SetHeader(); + func_enable = + Func::MakeValue(_func) | + WriteEnable::MakeValue(_write_enable ? 1 : 0) | + Enable::MakeValue(_enable ? 1 : 0); + } + + static void* Set(void* cmd, + Comparison func, bool write_enable, bool enable) { + static_cast<ValueType*>(cmd)->Init(func, write_enable, enable); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + uint32 func_enable; +}; + +COMPILE_ASSERT(sizeof(SetDepthTest) == 8, Sizeof_SetDepthTest_is_not_8); +COMPILE_ASSERT(offsetof(SetDepthTest, header) == 0, + OffsetOf_SetDepthTest_header_not_0); +COMPILE_ASSERT(offsetof(SetDepthTest, func_enable) == 4, + OffsetOf_SetDepthTest_func_enable_not_4); + +struct SetStencilTest { + typedef SetStencilTest ValueType; + static const CommandId kCmdId = kSetStencilTest; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 8> WriteMask; + typedef BitField<8, 8> CompareMask; + typedef BitField<16, 8> ReferenceValue; + typedef BitField<24, 6> Unused0; + typedef BitField<30, 1> SeparateCCW; + typedef BitField<31, 1> Enable; + // argument 1 + typedef BitField<0, 3> CWFunc; + typedef BitField<3, 3> CWPassOp; + typedef BitField<6, 3> CWFailOp; + typedef BitField<9, 3> CWZFailOp; + typedef BitField<12, 4> Unused1; + typedef BitField<16, 3> CCWFunc; + typedef BitField<19, 3> CCWPassOp; + typedef BitField<22, 3> CCWFailOp; + typedef BitField<25, 3> CCWZFailOp; + typedef BitField<28, 4> Unused2; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(uint8 _write_mask, + uint8 _compare_mask, + uint8 _reference_value, + bool _separate_ccw, + bool _enable, + Comparison _cw_func, + StencilOp _cw_pass_op, + StencilOp _cw_fail_op, + StencilOp _cw_z_fail_op, + Comparison _ccw_func, + StencilOp _ccw_pass_op, + StencilOp _ccw_fail_op, + StencilOp _ccw_z_fail_op) { + SetHeader(); + stencil_args0 = + WriteMask::MakeValue(_write_mask) | + CompareMask::MakeValue(_compare_mask) | + ReferenceValue::MakeValue(_reference_value) | + SeparateCCW::MakeValue(_separate_ccw ? 1 : 0) | + Enable::MakeValue(_enable ? 1 : 0); + stencil_args1 = + CWFunc::MakeValue(_cw_func) | + CWPassOp::MakeValue(_cw_pass_op) | + CWFailOp::MakeValue(_cw_fail_op) | + CWZFailOp::MakeValue(_cw_z_fail_op) | + CCWFunc::MakeValue(_ccw_func) | + CCWPassOp::MakeValue(_ccw_pass_op) | + CCWFailOp::MakeValue(_ccw_fail_op) | + CCWZFailOp::MakeValue(_ccw_z_fail_op); + } + + static void* Set( + void* cmd, + uint8 write_mask, + uint8 compare_mask, + uint8 reference_value, + bool separate_ccw, + bool enable, + Comparison cw_func, + StencilOp cw_pass_op, + StencilOp cw_fail_op, + StencilOp cw_z_fail_op, + Comparison ccw_func, + StencilOp ccw_pass_op, + StencilOp ccw_fail_op, + StencilOp ccw_z_fail_op) { + static_cast<ValueType*>(cmd)->Init( + write_mask, + compare_mask, + reference_value, + separate_ccw, + enable, + cw_func, + cw_pass_op, + cw_fail_op, + cw_z_fail_op, + ccw_func, + ccw_pass_op, + ccw_fail_op, + ccw_z_fail_op); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + uint32 stencil_args0; + uint32 stencil_args1; +}; + +COMPILE_ASSERT(sizeof(SetStencilTest) == 12, + Sizeof_SetStencilTest_is_not_12); +COMPILE_ASSERT(offsetof(SetStencilTest, header) == 0, + OffsetOf_SetStencilTest_header_not_0); +COMPILE_ASSERT(offsetof(SetStencilTest, stencil_args0) == 4, + OffsetOf_SetStencilTest_stencil_args0_not_4); +COMPILE_ASSERT(offsetof(SetStencilTest, stencil_args1) == 8, + OffsetOf_SetStencilTest_stencil_args1_not_8); + +struct SetColorWrite { + typedef SetColorWrite ValueType; + static const CommandId kCmdId = kSetColorWrite; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 1> RedMask; + typedef BitField<1, 1> GreenMask; + typedef BitField<2, 1> BlueMask; + typedef BitField<3, 1> AlphaMask; + typedef BitField<0, 4> AllColorsMask; // alias for RGBA + typedef BitField<4, 27> Unused; + typedef BitField<31, 1> DitherEnable; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(uint8 _mask, bool _dither_enable) { + SetHeader(); + flags = + RedMask::MakeValue((_mask | 0x01) != 0 ? 1 : 0) | + GreenMask::MakeValue((_mask | 0x02) != 0 ? 1 : 0) | + BlueMask::MakeValue((_mask | 0x02) != 0 ? 1 : 0) | + AlphaMask::MakeValue((_mask | 0x02) != 0 ? 1 : 0) | + DitherEnable::MakeValue(_dither_enable ? 1 : 0); + } + + static void* Set(void* cmd, uint8 mask, bool dither_enable) { + static_cast<ValueType*>(cmd)->Init(mask, dither_enable); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + uint32 flags; +}; + +COMPILE_ASSERT(sizeof(SetColorWrite) == 8, Sizeof_SetColorWrite_is_not_8); +COMPILE_ASSERT(offsetof(SetColorWrite, header) == 0, + OffsetOf_SetColorWrite_header_not_0); +COMPILE_ASSERT(offsetof(SetColorWrite, flags) == 4, + OffsetOf_SetColorWrite_flags_not_4); + +struct SetBlending { + typedef SetBlending ValueType; + static const CommandId kCmdId = kSetBlending; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 0 + typedef BitField<0, 4> ColorSrcFunc; + typedef BitField<4, 4> ColorDstFunc; + typedef BitField<8, 3> ColorEq; + typedef BitField<11, 5> Unused0; + typedef BitField<16, 4> AlphaSrcFunc; + typedef BitField<20, 4> AlphaDstFunc; + typedef BitField<24, 3> AlphaEq; + typedef BitField<27, 3> Unused1; + typedef BitField<30, 1> SeparateAlpha; + typedef BitField<31, 1> Enable; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init( + BlendFunc _color_src_func, + BlendFunc _color_dst_func, + BlendEq _color_eq, + BlendFunc _alpha_src_func, + BlendFunc _alpha_dst_func, + BlendEq _alpha_eq, + bool _separate_alpha, + bool _enable) { + SetHeader(); + blend_settings = + ColorSrcFunc::MakeValue(_color_src_func) | + ColorDstFunc::MakeValue(_color_dst_func) | + ColorEq::MakeValue(_color_eq) | + AlphaSrcFunc::MakeValue(_alpha_src_func) | + AlphaDstFunc::MakeValue(_alpha_dst_func) | + AlphaEq::MakeValue(_alpha_eq) | + SeparateAlpha::MakeValue(_separate_alpha ? 1 : 0) | + Enable::MakeValue(_enable ? 1 : 0); + } + + static void* Set( + void* cmd, + BlendFunc color_src_func, + BlendFunc color_dst_func, + BlendEq color_eq, + BlendFunc alpha_src_func, + BlendFunc alpha_dst_func, + BlendEq alpha_eq, + bool separate_alpha, + bool enable) { + static_cast<ValueType*>(cmd)->Init( + color_src_func, + color_dst_func, + color_eq, + alpha_src_func, + alpha_dst_func, + alpha_eq, + separate_alpha, + enable); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + uint32 blend_settings; +}; + +COMPILE_ASSERT(sizeof(SetBlending) == 8, Sizeof_SetBlending_is_not_8); +COMPILE_ASSERT(offsetof(SetBlending, header) == 0, + OffsetOf_SetBlending_header_not_0); +COMPILE_ASSERT(offsetof(SetBlending, blend_settings) == 4, + OffsetOf_SetBlending_blend_settings_not_4); + +struct SetBlendingColor { + typedef SetBlendingColor ValueType; + static const CommandId kCmdId = kSetBlendingColor; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(float _red, float _green, float _blue, float _alpha) { + SetHeader(); + red = _red; + green = _green; + blue = _blue; + alpha = _alpha; + } + + static void* Set(void* cmd, + float red, float green, float blue, float alpha) { + static_cast<ValueType*>(cmd)->Init(red, green, blue, alpha); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + float red; + float blue; + float green; + float alpha; +}; + +COMPILE_ASSERT(sizeof(SetBlendingColor) == 20, + Sizeof_SetBlendingColor_is_not_20); +COMPILE_ASSERT(offsetof(SetBlendingColor, header) == 0, + OffsetOf_SetBlendingColor_header_not_0); +COMPILE_ASSERT(offsetof(SetBlendingColor, red) == 4, + OffsetOf_SetBlendingColor_red_not_4); +COMPILE_ASSERT(offsetof(SetBlendingColor, blue) == 8, + OffsetOf_SetBlendingColor_blue_not_8); +COMPILE_ASSERT(offsetof(SetBlendingColor, green) == 12, + OffsetOf_SetBlendingColor_green_not_12); +COMPILE_ASSERT(offsetof(SetBlendingColor, alpha) == 16, + OffsetOf_SetBlendingColor_alpha_not_16); + +struct CreateRenderSurface { + typedef CreateRenderSurface ValueType; + static const CommandId kCmdId = kCreateRenderSurface; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 1 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + // argument 2 may refer to side or depth + typedef BitField<0, 16> Levels; + typedef BitField<16, 16> Side; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _render_surface_id, + ResourceId _texture_id, uint32 _width, uint32 _height, + uint32 _level, uint32 _side) { + SetHeader(); + render_surface_id = _render_surface_id; + // TODO(gman): Why does this need a width and height. It's inherited from + // the texture isn't it? + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + levels_side = Levels::MakeValue(_level) | Side::MakeValue(_side); + texture_id = _texture_id; + } + + static void* Set(void* cmd, + ResourceId render_surface_id, ResourceId texture_id, + uint32 width, uint32 height, + uint32 level, uint32 side) { + static_cast<ValueType*>(cmd)->Init(render_surface_id, texture_id, + width, height, + level, side); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId render_surface_id; + uint32 width_height; + uint32 levels_side; + ResourceId texture_id; +}; + +COMPILE_ASSERT(sizeof(CreateRenderSurface) == 20, + Sizeof_CreateRenderSurface_is_not_20); +COMPILE_ASSERT(offsetof(CreateRenderSurface, header) == 0, + OffsetOf_CreateRenderSurface_header_not_0); +COMPILE_ASSERT(offsetof(CreateRenderSurface, render_surface_id) == 4, + OffsetOf_CreateRenderSurface_render_surface_id_not_4); +COMPILE_ASSERT(offsetof(CreateRenderSurface, width_height) == 8, + OffsetOf_CreateRenderSurface_width_height_not_8); +COMPILE_ASSERT(offsetof(CreateRenderSurface, levels_side) == 12, + OffsetOf_CreateRenderSurface_levels_side_not_12); +COMPILE_ASSERT(offsetof(CreateRenderSurface, texture_id) == 16, + OffsetOf_CreateRenderSurface_texture_id_not_16); + +struct DestroyRenderSurface { + typedef DestroyRenderSurface ValueType; + static const CommandId kCmdId = kDestroyRenderSurface; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _render_surface_id) { + SetHeader(); + render_surface_id = _render_surface_id; + } + + static void* Set(void* cmd, ResourceId render_surface_id) { + static_cast<ValueType*>(cmd)->Init(render_surface_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId render_surface_id; +}; + +COMPILE_ASSERT(sizeof(DestroyRenderSurface) == 8, + Sizeof_DestroyRenderSurface_is_not_8); +COMPILE_ASSERT(offsetof(DestroyRenderSurface, header) == 0, + OffsetOf_DestroyRenderSurface_header_not_0); +COMPILE_ASSERT(offsetof(DestroyRenderSurface, render_surface_id) == 4, + OffsetOf_DestroyRenderSurface_render_surface_id_not_4); + +struct CreateDepthSurface { + typedef CreateDepthSurface ValueType; + static const CommandId kCmdId = kCreateDepthSurface; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + // argument 1 + typedef BitField<0, 16> Width; + typedef BitField<16, 16> Height; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _depth_surface_id, uint32 _width, uint32 _height) { + SetHeader(); + depth_surface_id = _depth_surface_id; + width_height = Width::MakeValue(_width) | Height::MakeValue(_height); + } + + static void* Set(void* cmd, ResourceId depth_surface_id, + uint32 width, uint32 height) { + static_cast<ValueType*>(cmd)->Init(depth_surface_id, width, height); + return NextCmdAddress<ValueType>(cmd); + } + + // TODO(gman): fix this to not use obfusticated fields. + CommandHeader header; + ResourceId depth_surface_id; + uint32 width_height; +}; + +COMPILE_ASSERT(sizeof(CreateDepthSurface) == 12, + Sizeof_CreateDepthSurface_is_not_12); +COMPILE_ASSERT(offsetof(CreateDepthSurface, header) == 0, + OffsetOf_CreateDepthSurface_header_not_0); +COMPILE_ASSERT(offsetof(CreateDepthSurface, depth_surface_id) == 4, + OffsetOf_CreateDepthSurface_depth_surface_id_not_4); +COMPILE_ASSERT(offsetof(CreateDepthSurface, width_height) == 8, + OffsetOf_CreateDepthSurface_width_height_not_8); + +struct DestroyDepthSurface { + typedef DestroyDepthSurface ValueType; + static const CommandId kCmdId = kDestroyDepthSurface; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _depth_surface_id) { + SetHeader(); + depth_surface_id = _depth_surface_id; + } + + static void* Set(void* cmd, ResourceId depth_surface_id) { + static_cast<ValueType*>(cmd)->Init(depth_surface_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId depth_surface_id; +}; + +COMPILE_ASSERT(sizeof(DestroyDepthSurface) == 8, + Sizeof_DestroyDepthSurface_is_not_8); +COMPILE_ASSERT(offsetof(DestroyDepthSurface, header) == 0, + OffsetOf_DestroyDepthSurface_header_not_0); +COMPILE_ASSERT(offsetof(DestroyDepthSurface, depth_surface_id) == 4, + OffsetOf_DestroyDepthdepth_surface_id_not_4); + +struct SetRenderSurface { + typedef SetRenderSurface ValueType; + static const CommandId kCmdId = kSetRenderSurface; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init(ResourceId _render_surface_id, ResourceId _depth_surface_id) { + SetHeader(); + render_surface_id = _render_surface_id; + depth_surface_id = _depth_surface_id; + } + + static void* Set(void* cmd, + ResourceId render_surface_id, ResourceId depth_surface_id) { + static_cast<ValueType*>(cmd)->Init(render_surface_id, depth_surface_id); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; + ResourceId render_surface_id; + ResourceId depth_surface_id; +}; + +COMPILE_ASSERT(sizeof(SetRenderSurface) == 12, + Sizeof_SetRenderSurface_is_not_12); +COMPILE_ASSERT(offsetof(SetRenderSurface, header) == 0, + OffsetOf_SetRenderSurface_header_not_0); +COMPILE_ASSERT(offsetof(SetRenderSurface, render_surface_id) == 4, + OffsetOf_SetRenderSurface_render_surface_id_not_4); +COMPILE_ASSERT(offsetof(SetRenderSurface, depth_surface_id) == 8, + OffsetOf_SetRenderSurface_depth_surface_id_not_8); + +struct SetBackSurfaces { + typedef SetBackSurfaces ValueType; + static const CommandId kCmdId = kSetBackSurfaces; + static const cmd::ArgFlags kArgFlags = cmd::kFixed; + + void SetHeader() { + header.SetCmd<ValueType>(); + } + + void Init() { + SetHeader(); + } + + static void* Set(void* cmd) { + static_cast<ValueType*>(cmd)->Init(); + return NextCmdAddress<ValueType>(cmd); + } + + CommandHeader header; +}; + +COMPILE_ASSERT(sizeof(SetBackSurfaces) == 4, + Sizeof_SetBackSurfaces_is_not_4); +COMPILE_ASSERT(offsetof(SetBackSurfaces, header) == 0, + OffsetOf_SetBackSurfaces_header_not_0); + +#pragma pack(pop) + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_FORMAT_H_ diff --git a/o3d/gpu/command_buffer/common/resource.cc b/o3d/gpu/command_buffer/common/resource.cc new file mode 100644 index 0000000..f3b75ec --- /dev/null +++ b/o3d/gpu/command_buffer/common/resource.cc @@ -0,0 +1,120 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the helper functions for resources. + +#include "gpu/command_buffer/common/resource.h" + +namespace command_buffer { + +namespace texture { + +// Gets the number of bytes per block for a given format. +unsigned int GetBytesPerBlock(Format format) { + switch (format) { + case kXRGB8: + case kARGB8: + case kR32F: + return 4; + case kABGR16F: + return 8; + case kABGR32F: + return 16; + case kDXT1: + return 8; + default: + // TODO(petersont): Add DXT3/5 support. + LOG(FATAL) << "Invalid format"; + return 1; + } +} + +// Gets the width of a block for a given format. +unsigned int GetBlockSizeX(Format format) { + switch (format) { + case kXRGB8: + case kARGB8: + case kABGR16F: + case kR32F: + case kABGR32F: + return 1; + case kDXT1: + return 4; + default: + // TODO(petersont): Add DXT3/5 support. + LOG(FATAL) << "Invalid format"; + return 1; + } +} + +// Gets the height of a block for a given format. +unsigned int GetBlockSizeY(Format format) { + // NOTE: currently only supported formats use square blocks. + return GetBlockSizeX(format); +} + +} // namespace texture + +namespace effect_param { + +// Gets the size of the data of a given parameter type. +unsigned int GetDataSize(DataType type) { + switch (type) { + case kUnknown: + return 0; + case kFloat1: + return sizeof(float); // NOLINT + case kFloat2: + return sizeof(float) * 2; // NOLINT + case kFloat3: + return sizeof(float) * 3; // NOLINT + case kFloat4: + return sizeof(float) * 4; // NOLINT + case kMatrix4: + return sizeof(float) * 16; // NOLINT + case kInt: + return sizeof(int); // NOLINT + case kBool: + return sizeof(bool); // NOLINT + case kSampler: + return sizeof(ResourceId); // NOLINT + case kTexture: + return sizeof(ResourceId); // NOLINT + default: + LOG(FATAL) << "Invalid type."; + return 0; + } +} + +} // namespace effect_param + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/common/resource.h b/o3d/gpu/command_buffer/common/resource.h new file mode 100644 index 0000000..01de6a1 --- /dev/null +++ b/o3d/gpu/command_buffer/common/resource.h @@ -0,0 +1,229 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains definitions for resource flags, enums, and helper +// functions. + +#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_RESOURCE_H_ +#define GPU_COMMAND_BUFFER_COMMON_CROSS_RESOURCE_H_ + +#include <algorithm> +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "gpu/command_buffer/common/types.h" +#include "gpu/command_buffer/common/logging.h" + +namespace command_buffer { + +// A resource ID, key to the resource maps. +typedef uint32 ResourceId; +// Invalid resource ID. +static const ResourceId kInvalidResource = 0xffffffffU; + +namespace vertex_buffer { +// Vertex buffer flags. +enum Flags { + kNone = 0x00, + kDynamic = 0x01, // This vertex buffer is dynamic and is expected to have + // its data updated often. +}; +} // namespace vertex_buffer + +namespace index_buffer { +// Index buffer flags. +enum Flags { + kNone = 0x00, + kDynamic = 0x01, // This index buffer is dynamic and is expected to have + // its data updated often. + kIndex32Bit = 0x02, // Indices contained in this index buffer are 32 bits + // (unsigned int) instead of 16 bit (unsigned short). +}; +} // namespace index_buffer + +namespace vertex_struct { +// Semantics for input data. +enum Semantic { + kUnknownSemantic = -1, + kPosition = 0, + kNormal, + kColor, + kTexCoord, + kNumSemantics +}; + +// Input data types. +enum Type { + kFloat1, + kFloat2, + kFloat3, + kFloat4, + kUChar4N, + kNumTypes +}; +} // namespace vertex_struct + +namespace effect_param { +enum DataType { + kUnknown, // A parameter exists in the effect, but the type is not + // representable (e.g. MATRIX3x4). + kFloat1, + kFloat2, + kFloat3, + kFloat4, + kMatrix4, + kInt, + kBool, + kSampler, + kTexture, + kNumTypes, + kMake32Bit = 0x7fffffff, +}; +COMPILE_ASSERT(sizeof(DataType) == 4, DataType_should_be_32_bits); + +// Gets the size of the data of a particular type. +unsigned int GetDataSize(DataType type); + +// Structure describing a parameter, filled in by the +// GAPIInterface::GetParamDesc call. +struct Desc { + Uint32 size; // the total memory size needed for the complete + // description. + Uint32 name_offset; // the offset of the parameter name, relative to + // the beginning of the structure. May be 0 if the + // name doesn't fit into the memory buffer. + Uint32 name_size; // the size of the parameter name, including the + // terminating nul character. Will always be set + // even if the name doesn't fit into the buffer. + Uint32 semantic_offset; // the offset of the parameter semantic, relative + // to the beginning of the structure. May be 0 if + // the semantic doesn't fit into the memory + // buffer. + Uint32 semantic_size; // the size of the parameter semantic, including + // the terminating nul character. Will always be + // set even if the semantic doesn't fit into the + // buffer. + Uint32 num_elements; // the number of entries if the parameter is an array + // 0 otherwise. + DataType data_type; // the data type of the parameter. + Uint32 data_size; // the size of the parameter data, in bytes. +}; +} // namespace effect_param + +namespace effect_stream { +struct Desc { + Desc() + : semantic(vertex_struct::kUnknownSemantic), + semantic_index(0) {} + Desc(Uint32 semantic, Uint32 semantic_index) + : semantic(semantic), + semantic_index(semantic_index) {} + Uint32 semantic; // the semantic type + Uint32 semantic_index; +}; +} // namespace effect_stream + +namespace texture { +// Texture flags. +enum Flags { + kNone = 0x00, + kDynamic = 0x01, // This texture is dynamic and is expected to have + // its data updated often. +}; + +// Texel formats. +enum Format { + kUnknown, + kXRGB8, + kARGB8, + kABGR16F, + kR32F, + kABGR32F, + kDXT1 +}; + +// Texture type. +enum Type { + kTexture2d, + kTexture3d, + kTextureCube, +}; + +// Cube map face. +enum Face { + kFacePositiveX, + kFaceNegativeX, + kFacePositiveY, + kFaceNegativeY, + kFacePositiveZ, + kFaceNegativeZ, + kFaceNone = kFacePositiveX, // For non-cube maps. +}; + +// Gets the number of bytes per block for a given texture format. For most +// texture formats, a block is 1x1 texels, but DXT* formats have 4x4 blocks. +unsigned int GetBytesPerBlock(Format format); +// Gets the x dimension of a texel block for a given texture format. For most +// texture formats, a block is 1x1 texels, but DXT* formats have 4x4 blocks. +unsigned int GetBlockSizeX(Format format); +// Gets the y dimension of a texel block for a given texture format. For most +// texture formats, a block is 1x1 texels, but DXT* formats have 4x4 blocks. +unsigned int GetBlockSizeY(Format format); +// Gets the dimension of a mipmap level given the dimension of the base +// level. Every mipmap level is half the size of the previous level, rounding +// down. +inline unsigned int GetMipMapDimension(unsigned int base, + unsigned int level) { + DCHECK_GT(base, 0U); + return std::max(1U, base >> level); +} +} // namespace texture + +namespace sampler { +enum AddressingMode { + kWrap, + kMirrorRepeat, + kClampToEdge, + kClampToBorder, + kNumAddressingMode +}; + +enum FilteringMode { + kNone, + kPoint, + kLinear, + kNumFilteringMode +}; +} // namespace sampler + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_RESOURCE_H_ diff --git a/o3d/gpu/command_buffer/common/types.h b/o3d/gpu/command_buffer/common/types.h new file mode 100644 index 0000000..daa01cb --- /dev/null +++ b/o3d/gpu/command_buffer/common/types.h @@ -0,0 +1,60 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains cross-platform basic type definitions + +#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_TYPES_H_ +#define GPU_COMMAND_BUFFER_COMMON_CROSS_TYPES_H_ + +#include <build/build_config.h> +#if !defined(COMPILER_MSVC) +#include <stdint.h> +#endif +#include <string> + +namespace command_buffer { +#if defined(COMPILER_MSVC) +typedef short Int16; +typedef unsigned short Uint16; +typedef int Int32; +typedef unsigned int Uint32; +#else +typedef int16_t Int16; +typedef uint16_t Uint16; +typedef int32_t Int32; +typedef uint32_t Uint32; +#endif + +typedef std::string String; +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_TYPES_H_ diff --git a/o3d/gpu/command_buffer/service/big_test_main.cc b/o3d/gpu/command_buffer/service/big_test_main.cc new file mode 100644 index 0000000..cda7d0e --- /dev/null +++ b/o3d/gpu/command_buffer/service/big_test_main.cc @@ -0,0 +1,124 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the entry to the big test program, for linux. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/cross/big_test_helpers.h" +#include "gpu/command_buffer/service/cross/gl/gapi_gl.h" +#include "gpu/command_buffer/service/linux/x_utils.h" + +namespace command_buffer { + +String *g_program_path = NULL; +GAPIInterface *g_gapi = NULL; + +bool ProcessSystemMessages() { + return true; +} + +} // namespace command_buffer + +using o3d::String; +using o3d::command_buffer::g_program_path; +using o3d::command_buffer::g_gapi; +using o3d::command_buffer::GAPIGL; +using o3d::command_buffer::XWindowWrapper; + + +// Creates a GL-compatible window of specified dimensions. +Window CreateWindow(Display *display, unsigned int width, unsigned int height) { + int attribs[] = { + GLX_RGBA, + GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + None + }; + XVisualInfo *visualInfo = glXChooseVisual(display, + DefaultScreen(display), + attribs); + Window root_window = RootWindow(display, visualInfo->screen); + Colormap colorMap = XCreateColormap(display, root_window, visualInfo->visual, + AllocNone); + + XSetWindowAttributes windowAttributes; + windowAttributes.colormap = colorMap; + windowAttributes.border_pixel = 0; + windowAttributes.event_mask = StructureNotifyMask; + Window window = XCreateWindow(display, root_window, + 0, 0, width, height, 0, visualInfo->depth, + InputOutput, visualInfo->visual, + CWBorderPixel|CWColormap|CWEventMask, + &windowAttributes); + if (!window) return 0; + XMapWindow(display, window); + XSync(display, True); + return window; +} + +// Creates a window, initializes the GAPI instance. +int main(int argc, char *argv[]) { + String program_path = argv[0]; + + // Remove all characters starting with last '/'. + size_t backslash_pos = program_path.rfind('/'); + if (backslash_pos != String::npos) { + program_path.erase(backslash_pos); + } + g_program_path = &program_path; + + GAPIGL gl_gapi; + g_gapi = &gl_gapi; + + Display *display = XOpenDisplay(0); + if (!display) { + LOG(FATAL) << "Could not open the display."; + return 1; + } + + Window window = CreateWindow(display, 300, 300); + if (!window) { + LOG(FATAL) << "Could not create a window."; + return 1; + } + + XWindowWrapper wrapper(display, window); + gl_gapi.set_window_wrapper(&wrapper); + + int ret = big_test_main(argc, argv); + + g_gapi = NULL; + g_program_path = NULL; + return ret; +} diff --git a/o3d/gpu/command_buffer/service/cmd_buffer_engine.h b/o3d/gpu/command_buffer/service/cmd_buffer_engine.h new file mode 100644 index 0000000..74ad649 --- /dev/null +++ b/o3d/gpu/command_buffer/service/cmd_buffer_engine.h @@ -0,0 +1,70 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file defines the CommandBufferEngine class, providing the main loop for +// the service, exposing the RPC API, managing the command parser. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_CMD_BUFFER_ENGINE_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_CMD_BUFFER_ENGINE_H_ + +#include "base/basictypes.h" + +namespace command_buffer { + +class CommandBufferEngine { + public: + CommandBufferEngine() { + } + + virtual ~CommandBufferEngine() { + } + + // Gets the base address of a registered shared memory buffer. + // Parameters: + // shm_id: the identifier for the shared memory buffer. + virtual void *GetSharedMemoryAddress(int32 shm_id) = 0; + + // Gets the size of a registered shared memory buffer. + // Parameters: + // shm_id: the identifier for the shared memory buffer. + virtual size_t GetSharedMemorySize(int32 shm_id) = 0; + + // Sets the token value. + virtual void set_token(int32 token) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(CommandBufferEngine); +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_CMD_BUFFER_ENGINE_H_ diff --git a/o3d/gpu/command_buffer/service/cmd_parser.cc b/o3d/gpu/command_buffer/service/cmd_parser.cc new file mode 100644 index 0000000..b3486ec --- /dev/null +++ b/o3d/gpu/command_buffer/service/cmd_parser.cc @@ -0,0 +1,112 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the command parser. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/cmd_parser.h" +// TODO(gman): remove this so we can use this code for different formats. +#include "gpu/command_buffer/common/o3d_cmd_format.h" + +namespace command_buffer { + +CommandParser::CommandParser(void *shm_address, + size_t shm_size, + ptrdiff_t offset, + size_t size, + CommandBufferOffset start_get, + AsyncAPIInterface *handler) + : get_(start_get), + put_(start_get), + handler_(handler) { + // check proper alignments. + DCHECK_EQ(0, (reinterpret_cast<intptr_t>(shm_address)) % 4); + DCHECK_EQ(0, offset % 4); + DCHECK_EQ(0u, size % 4); + // check that the command buffer fits into the memory buffer. + DCHECK_GE(shm_size, offset + size); + char * buffer_begin = static_cast<char *>(shm_address) + offset; + buffer_ = reinterpret_cast<CommandBufferEntry *>(buffer_begin); + entry_count_ = size / 4; +} + +// Process one command, reading the header from the command buffer, and +// forwarding the command index and the arguments to the handler. +// Note that: +// - validation needs to happen on a copy of the data (to avoid race +// conditions). This function only validates the header, leaving the arguments +// validation to the handler, so it can pass a reference to them. +// - get_ is modified *after* the command has been executed. +parse_error::ParseError CommandParser::ProcessCommand() { + CommandBufferOffset get = get_; + if (get == put_) return parse_error::kParseNoError; + + CommandHeader header = buffer_[get].value_header; + if (header.size == 0) { + DLOG(INFO) << "Error: zero sized command in command buffer"; + return parse_error::kParseInvalidSize; + } + + if (header.size + get > entry_count_) { + DLOG(INFO) << "Error: get offset out of bounds"; + return parse_error::kParseOutOfBounds; + } + + parse_error::ParseError result = handler_->DoCommand( + header.command, header.size - 1, buffer_ + get); + // TODO(gman): If you want to log errors this is the best place to catch them. + // It seems like we need an official way to turn on a debug mode and + // get these errors. + if (result != parse_error::kParseNoError) { + ReportError(header.command, result); + } + get_ = (get + header.size) % entry_count_; + return result; +} + +void CommandParser::ReportError(unsigned int command_id, + parse_error::ParseError result) { + DLOG(INFO) << "Error: " << result << " for Command " + << handler_->GetCommandName(command_id); +} + +// Processes all the commands, while the buffer is not empty. Stop if an error +// is encountered. +parse_error::ParseError CommandParser::ProcessAllCommands() { + while (!IsEmpty()) { + parse_error::ParseError error = ProcessCommand(); + if (error) return error; + } + return parse_error::kParseNoError; +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/cmd_parser.h b/o3d/gpu/command_buffer/service/cmd_parser.h new file mode 100644 index 0000000..2209cf8 --- /dev/null +++ b/o3d/gpu/command_buffer/service/cmd_parser.h @@ -0,0 +1,115 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the command parser class. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_CMD_PARSER_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_CMD_PARSER_H_ + +#include "gpu/command_buffer/common/constants.h" +#include "gpu/command_buffer/common/cmd_buffer_common.h" + +namespace command_buffer { + +class AsyncAPIInterface; + +// Command parser class. This class parses commands from a shared memory +// buffer, to implement some asynchronous RPC mechanism. +class CommandParser { + public: + CommandParser(void *shm_address, + size_t shm_size, + ptrdiff_t offset, + size_t size, + CommandBufferOffset start_get, + AsyncAPIInterface *handler); + + // Gets the "get" pointer. The get pointer is an index into the command + // buffer considered as an array of CommandBufferEntry. + CommandBufferOffset get() const { return get_; } + + // Sets the "put" pointer. The put pointer is an index into the command + // buffer considered as an array of CommandBufferEntry. + void set_put(CommandBufferOffset put) { put_ = put; } + + // Gets the "put" pointer. The put pointer is an index into the command + // buffer considered as an array of CommandBufferEntry. + CommandBufferOffset put() const { return put_; } + + // Checks whether there are commands to process. + bool IsEmpty() const { return put_ == get_; } + + // Processes one command, updating the get pointer. This will return an error + // if there are no commands in the buffer. + parse_error::ParseError ProcessCommand(); + + // Processes all commands until get == put. + parse_error::ParseError ProcessAllCommands(); + + // Reports an error. + void ReportError(unsigned int command_id, parse_error::ParseError result); + + private: + CommandBufferOffset get_; + CommandBufferOffset put_; + CommandBufferEntry *buffer_; + size_t entry_count_; + AsyncAPIInterface *handler_; +}; + +// This class defines the interface for an asynchronous API handler, that +// is responsible for de-multiplexing commands and their arguments. +class AsyncAPIInterface { + public: + AsyncAPIInterface() {} + virtual ~AsyncAPIInterface() {} + + // Executes a command. + // Parameters: + // command: the command index. + // arg_count: the number of CommandBufferEntry arguments. + // cmd_data: the command data. + // Returns: + // parse_error::NO_ERROR if no error was found, one of + // parse_error::ParseError otherwise. + virtual parse_error::ParseError DoCommand( + unsigned int command, + unsigned int arg_count, + const void* cmd_data) = 0; + + // Returns a name for a command. Useful for logging / debuging. + virtual const char* GetCommandName(unsigned int command_id) const = 0; +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_CMD_PARSER_H_ diff --git a/o3d/gpu/command_buffer/service/cmd_parser_test.cc b/o3d/gpu/command_buffer/service/cmd_parser_test.cc new file mode 100644 index 0000000..3c9871e --- /dev/null +++ b/o3d/gpu/command_buffer/service/cmd_parser_test.cc @@ -0,0 +1,316 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// Tests for the command parser. + +#include "gpu/command_buffer/service/precompile.h" + +#include "tests/common/win/testing_common.h" +#include "gpu/command_buffer/service/cmd_parser.h" +#include "gpu/command_buffer/service/mocks.h" +#include "base/scoped_ptr.h" + +namespace command_buffer { + +using testing::Return; +using testing::Mock; +using testing::Truly; +using testing::Sequence; +using testing::_; + +// Test fixture for CommandParser test - Creates a mock AsyncAPIInterface, and +// a fixed size memory buffer. Also provides a simple API to create a +// CommandParser. +class CommandParserTest : public testing::Test { + protected: + virtual void SetUp() { + api_mock_.reset(new AsyncAPIMock); + buffer_entry_count_ = 20; + buffer_.reset(new CommandBufferEntry[buffer_entry_count_]); + } + virtual void TearDown() {} + + // Adds a DoCommand expectation in the mock. + void AddDoCommandExpect(parse_error::ParseError _return, + unsigned int command, + unsigned int arg_count, + CommandBufferEntry *args) { + EXPECT_CALL(*api_mock(), DoCommand(command, arg_count, + Truly(AsyncAPIMock::IsArgs(arg_count, args)))) + .InSequence(sequence_) + .WillOnce(Return(_return)); + } + + // Creates a parser, with a buffer of the specified size (in entries). + CommandParser *MakeParser(unsigned int entry_count) { + size_t shm_size = buffer_entry_count_ * + sizeof(CommandBufferEntry); // NOLINT + size_t command_buffer_size = entry_count * + sizeof(CommandBufferEntry); // NOLINT + DCHECK_LE(command_buffer_size, shm_size); + return new CommandParser(buffer(), + shm_size, + 0, + command_buffer_size, + 0, + api_mock()); + } + + unsigned int buffer_entry_count() { return 20; } + AsyncAPIMock *api_mock() { return api_mock_.get(); } + CommandBufferEntry *buffer() { return buffer_.get(); } + private: + unsigned int buffer_entry_count_; + scoped_ptr<AsyncAPIMock> api_mock_; + scoped_array<CommandBufferEntry> buffer_; + Sequence sequence_; +}; + +// Tests initialization conditions. +TEST_F(CommandParserTest, TestInit) { + scoped_ptr<CommandParser> parser(MakeParser(10)); + EXPECT_EQ(0u, parser->get()); + EXPECT_EQ(0u, parser->put()); + EXPECT_TRUE(parser->IsEmpty()); +} + +// Tests simple commands. +TEST_F(CommandParserTest, TestSimple) { + scoped_ptr<CommandParser> parser(MakeParser(10)); + CommandBufferOffset put = parser->put(); + CommandHeader header; + + // add a single command, no args + header.size = 1; + header.command = 123; + buffer()[put++].value_header = header; + + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + + AddDoCommandExpect(parse_error::kParseNoError, 123, 0, NULL); + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessCommand()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); + + // add a single command, 2 args + header.size = 3; + header.command = 456; + buffer()[put++].value_header = header; + buffer()[put++].value_int32 = 2134; + buffer()[put++].value_float = 1.f; + + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + + CommandBufferEntry param_array[2]; + param_array[0].value_int32 = 2134; + param_array[1].value_float = 1.f; + AddDoCommandExpect(parse_error::kParseNoError, 456, 2, param_array); + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessCommand()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); +} + +// Tests having multiple commands in the buffer. +TEST_F(CommandParserTest, TestMultipleCommands) { + scoped_ptr<CommandParser> parser(MakeParser(10)); + CommandBufferOffset put = parser->put(); + CommandHeader header; + + // add 2 commands, test with single ProcessCommand() + header.size = 2; + header.command = 789; + buffer()[put++].value_header = header; + buffer()[put++].value_int32 = 5151; + + CommandBufferOffset put_cmd2 = put; + header.size = 2; + header.command = 2121; + buffer()[put++].value_header = header; + buffer()[put++].value_int32 = 3434; + + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + + CommandBufferEntry param_array[2]; + param_array[0].value_int32 = 5151; + AddDoCommandExpect(parse_error::kParseNoError, 789, 1, param_array); + param_array[1].value_int32 = 3434; + AddDoCommandExpect(parse_error::kParseNoError, 2121, 1, + param_array+1); + + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessCommand()); + EXPECT_EQ(put_cmd2, parser->get()); + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessCommand()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); + + // add 2 commands again, test with ProcessAllCommands() + header.size = 2; + header.command = 4545; + buffer()[put++].value_header = header; + buffer()[put++].value_int32 = 5656; + + header.size = 2; + header.command = 6767; + buffer()[put++].value_header = header; + buffer()[put++].value_int32 = 7878; + + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + + param_array[0].value_int32 = 5656; + AddDoCommandExpect(parse_error::kParseNoError, 4545, 1, param_array); + param_array[1].value_int32 = 7878; + AddDoCommandExpect(parse_error::kParseNoError, 6767, 1, + param_array+1); + + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessAllCommands()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); +} + +// Tests that the parser will wrap correctly at the end of the buffer. +TEST_F(CommandParserTest, TestWrap) { + scoped_ptr<CommandParser> parser(MakeParser(5)); + CommandBufferOffset put = parser->put(); + CommandHeader header; + + // add 3 commands with no args (1 word each) + for (unsigned int i = 0; i < 3; ++i) { + header.size = 1; + header.command = i; + buffer()[put++].value_header = header; + AddDoCommandExpect(parse_error::kParseNoError, i, 0, NULL); + } + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessAllCommands()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); + + // add 1 command with 1 arg (2 words). That should put us at the end of the + // buffer. + header.size = 2; + header.command = 3; + buffer()[put++].value_header = header; + buffer()[put++].value_int32 = 5; + CommandBufferEntry param; + param.value_int32 = 5; + AddDoCommandExpect(parse_error::kParseNoError, 3, 1, ¶m); + + DCHECK_EQ(5u, put); + put = 0; + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessAllCommands()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); + + // add 1 command with 1 arg (2 words). + header.size = 2; + header.command = 4; + buffer()[put++].value_header = header; + buffer()[put++].value_int32 = 6; + param.value_int32 = 6; + AddDoCommandExpect(parse_error::kParseNoError, 4, 1, ¶m); + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessAllCommands()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); +} + +// Tests error conditions. +TEST_F(CommandParserTest, TestError) { + scoped_ptr<CommandParser> parser(MakeParser(5)); + CommandBufferOffset put = parser->put(); + CommandHeader header; + + // Generate a command with size 0. + header.size = 0; + header.command = 3; + buffer()[put++].value_header = header; + + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(parse_error::kParseInvalidSize, + parser->ProcessAllCommands()); + // check that no DoCommand call was made. + Mock::VerifyAndClearExpectations(api_mock()); + + parser.reset(MakeParser(5)); + put = parser->put(); + + // Generate a command with size 6, extends beyond the end of the buffer. + header.size = 6; + header.command = 3; + buffer()[put++].value_header = header; + + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(parse_error::kParseOutOfBounds, + parser->ProcessAllCommands()); + // check that no DoCommand call was made. + Mock::VerifyAndClearExpectations(api_mock()); + + parser.reset(MakeParser(5)); + put = parser->put(); + + // Generates 2 commands. + header.size = 1; + header.command = 3; + buffer()[put++].value_header = header; + CommandBufferOffset put_post_fail = put; + header.size = 1; + header.command = 4; + buffer()[put++].value_header = header; + + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + // have the first command fail to parse. + AddDoCommandExpect(parse_error::kParseUnknownCommand, 3, 0, NULL); + EXPECT_EQ(parse_error::kParseUnknownCommand, + parser->ProcessAllCommands()); + // check that only one command was executed, and that get reflects that + // correctly. + EXPECT_EQ(put_post_fail, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); + // make the second one succeed, and check that the parser recovered fine. + AddDoCommandExpect(parse_error::kParseNoError, 4, 0, NULL); + EXPECT_EQ(parse_error::kParseNoError, parser->ProcessAllCommands()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/common_decoder.cc b/o3d/gpu/command_buffer/service/common_decoder.cc new file mode 100644 index 0000000..22bf445 --- /dev/null +++ b/o3d/gpu/command_buffer/service/common_decoder.cc @@ -0,0 +1,122 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/common_decoder.h" +#include "gpu/command_buffer/service/cmd_buffer_engine.h" + +namespace command_buffer { + +void* CommonDecoder::GetAddressAndCheckSize(unsigned int shm_id, + unsigned int offset, + unsigned int size) { + void* shm_addr = engine_->GetSharedMemoryAddress(shm_id); + if (!shm_addr) return NULL; + size_t shm_size = engine_->GetSharedMemorySize(shm_id); + unsigned int end = offset + size; + if (end > shm_size || end < offset) { + return NULL; + } + return static_cast<int8 *>(shm_addr) + offset; +} + +const char* CommonDecoder::GetCommonCommandName( + cmd::CommandId command_id) const { + return cmd::GetCommandName(command_id); +} + +namespace { + +// A struct to hold info about each command. +struct CommandInfo { + int arg_flags; // How to handle the arguments for this command + int arg_count; // How many arguments are expected for this command. +}; + +// A table of CommandInfo for all the commands. +const CommandInfo g_command_info[] = { + #define COMMON_COMMAND_BUFFER_CMD_OP(name) { \ + cmd::name::kArgFlags, \ + sizeof(cmd::name) / sizeof(CommandBufferEntry) - 1, }, /* NOLINT */ \ + + COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP) + + #undef COMMON_COMMAND_BUFFER_CMD_OP +}; + +} // anonymous namespace. + +// Decode command with its arguments, and call the corresponding method. +// Note: args is a pointer to the command buffer. As such, it could be changed +// by a (malicious) client at any time, so if validation has to happen, it +// should operate on a copy of them. +parse_error::ParseError CommonDecoder::DoCommonCommand( + unsigned int command, + unsigned int arg_count, + const void* cmd_data) { + if (command < arraysize(g_command_info)) { + const CommandInfo& info = g_command_info[command]; + unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count); + if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) || + (info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) { + switch (command) { + #define COMMON_COMMAND_BUFFER_CMD_OP(name) \ + case cmd::name::kCmdId: \ + return Handle ## name( \ + arg_count, \ + *static_cast<const cmd::name*>(cmd_data)); \ + + COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP) + + #undef COMMON_COMMAND_BUFFER_CMD_OP + } + } else { + return parse_error::kParseInvalidArguments; + } + } + return DoCommonCommand(command, arg_count, cmd_data); + return parse_error::kParseUnknownCommand; +} + +parse_error::ParseError CommonDecoder::HandleNoop( + uint32 arg_count, + const cmd::Noop& args) { + return parse_error::kParseNoError; +} + +parse_error::ParseError CommonDecoder::HandleSetToken( + uint32 arg_count, + const cmd::SetToken& args) { + engine_->set_token(args.token); + return parse_error::kParseNoError; +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/common_decoder.h b/o3d/gpu/command_buffer/service/common_decoder.h new file mode 100644 index 0000000..25d1dbe --- /dev/null +++ b/o3d/gpu/command_buffer/service/common_decoder.h @@ -0,0 +1,107 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_COMMON_DECODER_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_COMMON_DECODER_H_ + +#include "gpu/command_buffer/service/cmd_parser.h" + +namespace command_buffer { + +class CommandBufferEngine; + +// This class is a helper base class for implementing the common parts of the +// o3d/gl2 command buffer decoder. +class CommonDecoder : public AsyncAPIInterface { + public: + typedef parse_error::ParseError ParseError; + + CommonDecoder() : engine_(NULL) { + } + virtual ~CommonDecoder() { + } + + // Sets the engine, to get shared memory buffers from, and to set the token + // to. + void set_engine(CommandBufferEngine* engine) { + engine_ = engine; + } + + protected: + // Executes a common command. + // Parameters: + // command: the command index. + // arg_count: the number of CommandBufferEntry arguments. + // cmd_data: the command data. + // Returns: + // parse_error::NO_ERROR if no error was found, one of + // parse_error::ParseError otherwise. + parse_error::ParseError DoCommonCommand( + unsigned int command, + unsigned int arg_count, + const void* cmd_data); + + // Gets the address of shared memory data, given a shared memory ID and an + // offset. Also checks that the size is consistent with the shared memory + // size. + // Parameters: + // shm_id: the id of the shared memory buffer. + // offset: the offset of the data in the shared memory buffer. + // size: the size of the data. + // Returns: + // NULL if shm_id isn't a valid shared memory buffer ID or if the size + // check fails. Return a pointer to the data otherwise. + void* GetAddressAndCheckSize(unsigned int shm_id, + unsigned int offset, + unsigned int size); + + // Gets an name for a common command. + const char* GetCommonCommandName(cmd::CommandId command_id) const; + + private: + // Generate a member function prototype for each command in an automated and + // typesafe way. + #define COMMON_COMMAND_BUFFER_CMD_OP(name) \ + parse_error::ParseError Handle ## name( \ + unsigned int arg_count, \ + const cmd::name& args); \ + + COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP) + + #undef COMMON_COMMAND_BUFFER_CMD_OP + + CommandBufferEngine* engine_; +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_COMMON_DECODER_H_ + diff --git a/o3d/gpu/command_buffer/service/d3d9_utils.h b/o3d/gpu/command_buffer/service/d3d9_utils.h new file mode 100644 index 0000000..5938ac0 --- /dev/null +++ b/o3d/gpu/command_buffer/service/d3d9_utils.h @@ -0,0 +1,145 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file defines a few utilities for Direct3D. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_D3D9_UTILS_H_ +#define GPU_COMMAND_BUFFER_SERVICE_D3D9_UTILS_H_ + +#ifndef NOMINMAX +// windows.h defines min() and max() as macros, conflicting with std::min and +// std::max unless NOMINMAX is defined. +#define NOMINMAX +#endif +#include <windows.h> +#include <d3d9.h> +#include <d3dx9.h> +#include <dxerr.h> +#include <algorithm> +#include "gpu/command_buffer/common/gapi_interface.h" + +#if defined (_DEBUG) + +#ifndef HR +#define HR(x) { \ + HRESULT hr = x; \ + if (FAILED(hr)) { \ + LOG(ERROR) << "DirectX error at " << __FILE__ << ":" << __LINE__ \ + << " when calling " << #x << ": " << DXGetErrorStringA(hr); \ + } \ + } +#endif + +#else // _DEBUG + +#ifndef HR +#define HR(x) x; +#endif + +#endif // _DEBUG + +namespace command_buffer { + +union FloatAndDWORD { + float float_value; + DWORD dword_value; +}; + +// Bit casts a float into a DWORD. That's what D3D expects for some values. +inline DWORD FloatAsDWORD(float value) { + volatile FloatAndDWORD float_and_dword; + float_and_dword.float_value = value; + return float_and_dword.dword_value; +} + +// Clamps a float to [0 .. 1] and maps it to [0 .. 255] +inline unsigned int FloatToClampedByte(float value) { + value = std::min(1.f, std::max(0.f, value)); + return static_cast<unsigned int>(value * 255); +} + +// Converts a RGBA color into a D3DCOLOR +inline D3DCOLOR RGBAToD3DCOLOR(const o3d::RGBA &color) { + return D3DCOLOR_RGBA(FloatToClampedByte(color.red), + FloatToClampedByte(color.green), + FloatToClampedByte(color.blue), + FloatToClampedByte(color.alpha)); +} + +static bool D3DSemanticToCBSemantic( + D3DDECLUSAGE semantic, + unsigned int semantic_index, + vertex_struct::Semantic *out_semantic, + unsigned int *out_semantic_index) { + // TODO: what meaning do we really want to put to our semantics ? How + // do they match the semantics that are set in the effect ? What combination + // of (semantic, index) are supposed to work ? + // TODO(gman): This is just plain wrong! Fix it. Converting binormal to + // texcoord 7 means there will be conflicts if I have both a Binormal and a + // texcoord 7 or 2 binormals both of which we have examples of already in O3D! + switch (semantic) { + case D3DDECLUSAGE_POSITION: + if (semantic_index != 0) return false; + *out_semantic = vertex_struct::kPosition; + *out_semantic_index = 0; + return true; + case D3DDECLUSAGE_NORMAL: + if (semantic_index != 0) return false; + *out_semantic = vertex_struct::kNormal; + *out_semantic_index = 0; + return true; + case D3DDECLUSAGE_TANGENT: + if (semantic_index != 0) return false; + *out_semantic = vertex_struct::kTexCoord; + *out_semantic_index = 6; + return true; + case D3DDECLUSAGE_BINORMAL: + if (semantic_index != 0) return false; + *out_semantic = vertex_struct::kTexCoord; + *out_semantic_index = 7; + return true; + case D3DDECLUSAGE_COLOR: + if (semantic_index > 1) return false; + *out_semantic = vertex_struct::kColor; + *out_semantic_index = semantic_index; + return true; + case D3DDECLUSAGE_TEXCOORD: + *out_semantic = vertex_struct::kTexCoord; + *out_semantic_index = semantic_index; + return true; + default: + return false; + } +} +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_D3D9_UTILS_H_ diff --git a/o3d/gpu/command_buffer/service/effect_d3d9.cc b/o3d/gpu/command_buffer/service/effect_d3d9.cc new file mode 100644 index 0000000..4bfae53 --- /dev/null +++ b/o3d/gpu/command_buffer/service/effect_d3d9.cc @@ -0,0 +1,679 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the D3D9 versions of the +// Effect resource. +// This file also contains the related GAPID3D9 function implementations. + +#include "gpu/command_buffer/service/precompile.h" + +#include <algorithm> +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/geometry_d3d9.h" +#include "gpu/command_buffer/service/gapi_d3d9.h" +#include "gpu/command_buffer/service/effect_d3d9.h" +#include "gpu/command_buffer/service/sampler_d3d9.h" +#include "gpu/command_buffer/service/effect_utils.h" + +// TODO: remove link-dependency on D3DX. + +namespace command_buffer { +namespace o3d { + +// Logs the D3D effect error, from either the buffer, or GetLastError(). +static void LogFXError(LPD3DXBUFFER error_buffer) { + if (error_buffer) { + LPVOID compile_errors = error_buffer->GetBufferPointer(); + LOG(ERROR) << "Failed to compile effect: " + << static_cast<char *>(compile_errors); + } else { + HLOCAL hLocal = NULL; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + GetLastError(), + 0, + reinterpret_cast<wchar_t*>(&hLocal), + 0, + NULL); + wchar_t* msg = reinterpret_cast<wchar_t*>(LocalLock(hLocal)); + LOG(ERROR) << "Failed to compile effect: " << msg; + LocalFree(hLocal); + } +} + +EffectD3D9::EffectD3D9(GAPID3D9 *gapi, + ID3DXEffect *d3d_effect, + ID3DXConstantTable *fs_constant_table, + IDirect3DVertexShader9 *d3d_vertex_shader) + : gapi_(gapi), + d3d_effect_(d3d_effect), + fs_constant_table_(fs_constant_table), + d3d_vertex_shader_(d3d_vertex_shader), + sync_parameters_(false) { + for (unsigned int i = 0; i < kMaxSamplerUnits; ++i) { + samplers_[i] = kInvalidResource; + } + SetStreams(); +} +// Releases the D3D effect. +EffectD3D9::~EffectD3D9() { + for (ParamList::iterator it = params_.begin(); it != params_.end(); ++it) { + (*it)->ResetEffect(); + } + DCHECK(d3d_effect_); + d3d_effect_->Release(); + DCHECK(fs_constant_table_); + fs_constant_table_->Release(); + DCHECK(d3d_vertex_shader_); + d3d_vertex_shader_->Release(); +} + +// Compiles the effect, and checks that the effect conforms to what we expect +// (no extra technique or pass in the effect code, since one is implicitly added +// using the program entry points) and that it validates. If successful, wrap +// the D3D effect into a new EffectD3D9. +EffectD3D9 *EffectD3D9::Create(GAPID3D9 *gapi, + const String& effect_code, + const String& vertex_program_entry, + const String& fragment_program_entry) { + String prepared_effect = effect_code + + "technique Shaders { " + " pass p0 { " + " VertexShader = compile vs_2_0 " + vertex_program_entry + "();" + " PixelShader = compile ps_2_0 " + fragment_program_entry + "();" + " }" + "};"; + ID3DXEffect *d3d_effect = NULL; + LPD3DXBUFFER error_buffer; + IDirect3DDevice9 *device = gapi->d3d_device(); + if (gapi->D3DXCreateEffect(device, + prepared_effect.c_str(), + prepared_effect.size(), + NULL, + NULL, + 0, + NULL, + &d3d_effect, + &error_buffer) != D3D_OK) { + LogFXError(error_buffer); + return NULL; + } + // check that . + D3DXEFFECT_DESC effect_desc; + HR(d3d_effect->GetDesc(&effect_desc)); + if (effect_desc.Techniques != 1) { + LOG(ERROR) << "Only 1 technique is allowed in an effect."; + d3d_effect->Release(); + return NULL; + } + D3DXHANDLE technique = d3d_effect->GetTechnique(0); + DCHECK(technique); + if (d3d_effect->ValidateTechnique(technique) != D3D_OK) { + LOG(ERROR) << "Technique doesn't validate."; + d3d_effect->Release(); + return NULL; + } + D3DXTECHNIQUE_DESC technique_desc; + HR(d3d_effect->GetTechniqueDesc(technique, &technique_desc)); + if (technique_desc.Passes != 1) { + LOG(ERROR) << "Only 1 pass is allowed in an effect."; + d3d_effect->Release(); + return NULL; + } + d3d_effect->SetTechnique(technique); + D3DXHANDLE pass = d3d_effect->GetPass(technique, 0); + D3DXPASS_DESC pass_desc; + HR(d3d_effect->GetPassDesc(pass, &pass_desc)); + ID3DXConstantTable *table = NULL; + HR(gapi->D3DXGetShaderConstantTable(pass_desc.pPixelShaderFunction, + &table)); + if (!table) { + LOG(ERROR) << "Could not get the constant table."; + d3d_effect->Release(); + return NULL; + } + IDirect3DVertexShader9 *d3d_vertex_shader = NULL; + HR(device->CreateVertexShader(pass_desc.pVertexShaderFunction, + &d3d_vertex_shader)); + if (!d3d_vertex_shader) { + d3d_effect->Release(); + table->Release(); + DLOG(ERROR) << "Failed to create vertex shader"; + return NULL; + } + + return new EffectD3D9(gapi, d3d_effect, table, d3d_vertex_shader); +} + +// Begins rendering with the effect, setting all the appropriate states. +bool EffectD3D9::Begin() { + UINT numpasses; + HR(d3d_effect_->Begin(&numpasses, 0)); + HR(d3d_effect_->BeginPass(0)); + sync_parameters_ = false; + return SetSamplers(); +} + +// Terminates rendering with the effect, resetting all the appropriate states. +void EffectD3D9::End() { + HR(d3d_effect_->EndPass()); + HR(d3d_effect_->End()); +} + +// Gets the parameter count from the D3D effect description. +unsigned int EffectD3D9::GetParamCount() { + D3DXEFFECT_DESC effect_desc; + HR(d3d_effect_->GetDesc(&effect_desc)); + return effect_desc.Parameters; +} + +// Gets the number of input streams from the shader. +unsigned int EffectD3D9::GetStreamCount() { + return streams_.size(); +} + +// Retrieves the matching DataType from a D3D parameter description. +static effect_param::DataType GetDataTypeFromD3D( + const D3DXPARAMETER_DESC &desc) { + switch (desc.Type) { + case D3DXPT_FLOAT: + switch (desc.Class) { + case D3DXPC_SCALAR: + return effect_param::kFloat1; + case D3DXPC_VECTOR: + switch (desc.Columns) { + case 2: + return effect_param::kFloat2; + case 3: + return effect_param::kFloat3; + case 4: + return effect_param::kFloat4; + default: + return effect_param::kUnknown; + } + case D3DXPC_MATRIX_ROWS: + case D3DXPC_MATRIX_COLUMNS: + if (desc.Columns == 4 && desc.Rows == 4) { + return effect_param::kMatrix4; + } else { + return effect_param::kUnknown; + } + default: + return effect_param::kUnknown; + } + case D3DXPT_INT: + if (desc.Class == D3DXPC_SCALAR) { + return effect_param::kInt; + } else { + return effect_param::kUnknown; + } + case D3DXPT_BOOL: + if (desc.Class == D3DXPC_SCALAR) { + return effect_param::kBool; + } else { + return effect_param::kUnknown; + } + case D3DXPT_SAMPLER: + case D3DXPT_SAMPLER2D: + case D3DXPT_SAMPLER3D: + case D3DXPT_SAMPLERCUBE: + if (desc.Class == D3DXPC_OBJECT) { + return effect_param::kSampler; + } else { + return effect_param::kUnknown; + } + case D3DXPT_TEXTURE: + case D3DXPT_TEXTURE1D: + case D3DXPT_TEXTURE2D: + case D3DXPT_TEXTURE3D: + case D3DXPT_TEXTURECUBE: + if (desc.Class == D3DXPC_OBJECT) { + return effect_param::kTexture; + } else { + return effect_param::kUnknown; + } + default: + return effect_param::kUnknown; + } +} + +// Gets a handle to the selected parameter, and wraps it into an +// EffectParamD3D9 if successful. +EffectParamD3D9 *EffectD3D9::CreateParam(unsigned int index) { + D3DXHANDLE handle = d3d_effect_->GetParameter(NULL, index); + if (!handle) return NULL; + return EffectParamD3D9::Create(this, handle); +} + +// Gets a handle to the selected parameter, and wraps it into an +// EffectParamD3D9 if successful. +EffectParamD3D9 *EffectD3D9::CreateParamByName(const char *name) { + D3DXHANDLE handle = d3d_effect_->GetParameterByName(NULL, name); + if (!handle) return NULL; + return EffectParamD3D9::Create(this, handle); +} + +bool EffectD3D9::CommitParameters() { + if (sync_parameters_) { + sync_parameters_ = false; + d3d_effect_->CommitChanges(); + return SetSamplers(); + } else { + return true; + } +} + +bool EffectD3D9::SetSamplers() { + IDirect3DDevice9 *d3d_device = gapi_->d3d_device(); + bool result = true; + for (unsigned int i = 0; i < kMaxSamplerUnits; ++i) { + SamplerD3D9 *sampler = gapi_->GetSampler(samplers_[i]); + if (sampler) { + result &= sampler->ApplyStates(gapi_, i); + } else { + HR(d3d_device->SetTexture(i, NULL)); + } + } + return result; +} + +bool EffectD3D9::SetStreams() { + if (!d3d_vertex_shader_) { + return false; + } + UINT size; + d3d_vertex_shader_->GetFunction(NULL, &size); + scoped_array<DWORD> function(new DWORD[size]); + d3d_vertex_shader_->GetFunction(function.get(), &size); + + UINT num_semantics; + HR(gapi_->D3DXGetShaderInputSemantics(function.get(), + NULL, + &num_semantics)); + scoped_array<D3DXSEMANTIC> semantics(new D3DXSEMANTIC[num_semantics]); + HR(gapi_->D3DXGetShaderInputSemantics(function.get(), + semantics.get(), + &num_semantics)); + + streams_.resize(num_semantics); + for (UINT i = 0; i < num_semantics; ++i) { + vertex_struct::Semantic semantic; + unsigned int semantic_index; + if (D3DSemanticToCBSemantic(static_cast<D3DDECLUSAGE>(semantics[i].Usage), + static_cast<int>(semantics[i].UsageIndex), + &semantic, &semantic_index)) { + streams_[i].semantic = semantic; + streams_[i].semantic_index = semantic_index; + } + } + return true; +} + +void EffectD3D9::LinkParam(EffectParamD3D9 *param) { + params_.push_back(param); +} + +void EffectD3D9::UnlinkParam(EffectParamD3D9 *param) { + std::remove(params_.begin(), params_.end(), param); +} + +// Fills the Desc structure, appending name and semantic if any, and if enough +// room is available in the buffer. +bool EffectD3D9::GetStreamDesc(unsigned int index, + unsigned int size, + void *data) { + using effect_stream::Desc; + if (size < sizeof(Desc)) // NOLINT + return false; + + Desc *desc = static_cast<Desc *>(data); + *desc = streams_[index]; + return true; +} + +EffectParamD3D9::EffectParamD3D9(effect_param::DataType data_type, + EffectD3D9 *effect, + D3DXHANDLE handle) + : EffectParam(data_type), + effect_(effect), + handle_(handle), + sampler_units_(NULL), + sampler_unit_count_(0) { + DCHECK(effect_); + effect_->LinkParam(this); +} + +EffectParamD3D9::~EffectParamD3D9() { + if (effect_) effect_->UnlinkParam(this); +} + +EffectParamD3D9 *EffectParamD3D9::Create(EffectD3D9 *effect, + D3DXHANDLE handle) { + DCHECK(effect); + D3DXPARAMETER_DESC desc; + HR(effect->d3d_effect_->GetParameterDesc(handle, &desc)); + effect_param::DataType data_type = GetDataTypeFromD3D(desc); + EffectParamD3D9 *param = new EffectParamD3D9(data_type, effect, handle); + if (data_type == effect_param::kSampler) { + ID3DXConstantTable *table = effect->fs_constant_table_; + DCHECK(table); + D3DXHANDLE sampler_handle = table->GetConstantByName(NULL, desc.Name); + if (sampler_handle) { + D3DXCONSTANT_DESC desc_array[kMaxSamplerUnits]; + unsigned int num_desc = kMaxSamplerUnits; + table->GetConstantDesc(sampler_handle, desc_array, &num_desc); + // We have no good way of querying how many descriptions would really be + // returned as we're capping the number to kMaxSamplerUnits (which should + // be more than sufficient). If however we do end up with the max number + // there's a chance that there were actually more so let's log it. + if (num_desc == kMaxSamplerUnits) { + DLOG(WARNING) << "Number of constant descriptions might have exceeded " + << "the maximum of " << kMaxSamplerUnits; + } + param->sampler_unit_count_ = 0; + if (num_desc > 0) { + param->sampler_units_.reset(new unsigned int[num_desc]); + for (unsigned int desc_index = 0; desc_index < num_desc; desc_index++) { + D3DXCONSTANT_DESC constant_desc = desc_array[desc_index]; + if (constant_desc.Class == D3DXPC_OBJECT && + (constant_desc.Type == D3DXPT_SAMPLER || + constant_desc.Type == D3DXPT_SAMPLER2D || + constant_desc.Type == D3DXPT_SAMPLER3D || + constant_desc.Type == D3DXPT_SAMPLERCUBE)) { + param->sampler_units_[param->sampler_unit_count_++] = + constant_desc.RegisterIndex; + } + } + } + } + // if the sampler hasn't been found in the constant table, that means it + // isn't referenced, hence it doesn't use any sampler unit. + } + return param; +} + +// Fills the Desc structure, appending name and semantic if any, and if enough +// room is available in the buffer. +bool EffectParamD3D9::GetDesc(unsigned int size, void *data) { + using effect_param::Desc; + if (size < sizeof(Desc)) // NOLINT + return false; + if (!effect_) + return false; + ID3DXEffect *d3d_effect = effect_->d3d_effect_; + D3DXPARAMETER_DESC d3d_desc; + HR(d3d_effect->GetParameterDesc(handle_, &d3d_desc)); + unsigned int name_size = + d3d_desc.Name ? static_cast<unsigned int>(strlen(d3d_desc.Name)) + 1 : 0; + unsigned int semantic_size = d3d_desc.Semantic ? + static_cast<unsigned int>(strlen(d3d_desc.Semantic)) + 1 : 0; + unsigned int total_size = sizeof(Desc) + name_size + semantic_size; // NOLINT + + Desc *desc = static_cast<Desc *>(data); + memset(desc, 0, sizeof(*desc)); + desc->size = total_size; + desc->data_type = data_type(); + desc->data_size = GetDataSize(data_type()); + desc->name_offset = 0; + desc->name_size = name_size; + desc->num_elements = d3d_desc.Elements; + desc->semantic_offset = 0; + desc->semantic_size = semantic_size; + unsigned int current_offset = sizeof(Desc); + if (d3d_desc.Name && current_offset + name_size <= size) { + desc->name_offset = current_offset; + memcpy(static_cast<char *>(data) + current_offset, + d3d_desc.Name, name_size); + current_offset += name_size; + } + if (d3d_desc.Semantic && current_offset + semantic_size <= size) { + desc->semantic_offset = current_offset; + memcpy(static_cast<char *>(data) + current_offset, + d3d_desc.Semantic, semantic_size); + current_offset += semantic_size; + } + return true; +} + +// Sets the data into the D3D effect parameter, using the appropriate D3D call. +bool EffectParamD3D9::SetData(GAPID3D9 *gapi, + unsigned int size, + const void * data) { + if (!effect_) + return false; + ID3DXEffect *d3d_effect = effect_->d3d_effect_; + effect_param::DataType type = data_type(); + if (size < effect_param::GetDataSize(type)) return false; + switch (type) { + case effect_param::kFloat1: + HR(d3d_effect->SetFloat(handle_, *static_cast<const float *>(data))); + break; + case effect_param::kFloat2: + HR(d3d_effect->SetFloatArray(handle_, static_cast<const float *>(data), + 2)); + break; + case effect_param::kFloat3: + HR(d3d_effect->SetFloatArray(handle_, static_cast<const float *>(data), + 3)); + break; + case effect_param::kFloat4: + HR(d3d_effect->SetFloatArray(handle_, static_cast<const float *>(data), + 4)); + break; + case effect_param::kMatrix4: + HR(d3d_effect->SetMatrix(handle_, + reinterpret_cast<const D3DXMATRIX *>(data))); + break; + case effect_param::kInt: + HR(d3d_effect->SetInt(handle_, *static_cast<const int *>(data))); + break; + case effect_param::kBool: + HR(d3d_effect->SetBool(handle_, *static_cast<const bool *>(data)?1:0)); + break; + case effect_param::kSampler: { + ResourceId id = *static_cast<const ResourceId *>(data); + for (unsigned int i = 0; i < sampler_unit_count_; ++i) { + effect_->samplers_[sampler_units_[i]] = id; + } + break; + } + case effect_param::kTexture: { + // TODO(rlp): finish + break; + } + default: + DLOG(ERROR) << "Invalid parameter type."; + return false; + } + if (effect_ == gapi->current_effect()) { + effect_->sync_parameters_ = true; + } + return true; +} + +// Calls EffectD3D9::Create, and assign the result to the resource ID. +// If changing the current effect, dirty it. +parse_error::ParseError GAPID3D9::CreateEffect( + ResourceId id, + unsigned int size, + const void *data) { + if (id == current_effect_id_) DirtyEffect(); + // Even though Assign would Destroy the effect at id, we do it explicitly in + // case the creation fails. + effects_.Destroy(id); + // Data is vp_main \0 fp_main \0 effect_text. + String vertex_program_entry; + String fragment_program_entry; + String effect_code; + if (!ParseEffectData(size, data, + &vertex_program_entry, + &fragment_program_entry, + &effect_code)) { + return parse_error::kParseInvalidArguments; + } + EffectD3D9 * effect = EffectD3D9::Create(this, effect_code, + vertex_program_entry, + fragment_program_entry); + if (!effect) return parse_error::kParseInvalidArguments; + effects_.Assign(id, effect); + return parse_error::kParseNoError; +} + +// Destroys the Effect resource. +// If destroying the current effect, dirty it. +parse_error::ParseError GAPID3D9::DestroyEffect(ResourceId id) { + if (id == current_effect_id_) DirtyEffect(); + return effects_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Sets the current effect ID, dirtying the current effect. +parse_error::ParseError GAPID3D9::SetEffect(ResourceId id) { + DirtyEffect(); + current_effect_id_ = id; + return parse_error::kParseNoError; +} + +// Gets the param count from the effect and store it in the memory buffer. +parse_error::ParseError GAPID3D9::GetParamCount( + ResourceId id, + unsigned int size, + void *data) { + EffectD3D9 *effect = effects_.Get(id); + if (!effect || size < sizeof(Uint32)) // NOLINT + return parse_error::kParseInvalidArguments; + *static_cast<Uint32 *>(data) = effect->GetParamCount(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::CreateParam( + ResourceId param_id, + ResourceId effect_id, + unsigned int index) { + EffectD3D9 *effect = effects_.Get(effect_id); + if (!effect) return parse_error::kParseInvalidArguments; + EffectParamD3D9 *param = effect->CreateParam(index); + if (!param) return parse_error::kParseInvalidArguments; + effect_params_.Assign(param_id, param); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::CreateParamByName( + ResourceId param_id, + ResourceId effect_id, + unsigned int size, + const void *name) { + EffectD3D9 *effect = effects_.Get(effect_id); + if (!effect) return parse_error::kParseInvalidArguments; + std::string string_name(static_cast<const char *>(name), size); + EffectParamD3D9 *param = effect->CreateParamByName(string_name.c_str()); + if (!param) return parse_error::kParseInvalidArguments; + effect_params_.Assign(param_id, param); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::DestroyParam(ResourceId id) { + return effect_params_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPID3D9::SetParamData( + ResourceId id, + unsigned int size, + const void *data) { + EffectParamD3D9 *param = effect_params_.Get(id); + if (!param) return parse_error::kParseInvalidArguments; + return param->SetData(this, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPID3D9::GetParamDesc( + ResourceId id, + unsigned int size, + void *data) { + EffectParamD3D9 *param = effect_params_.Get(id); + if (!param) return parse_error::kParseInvalidArguments; + return param->GetDesc(size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Gets the stream count from the effect and stores it in the memory buffer. +parse_error::ParseError GAPID3D9::GetStreamCount( + ResourceId id, + unsigned int size, + void *data) { + EffectD3D9 *effect = effects_.Get(id); + if (!effect || size < sizeof(Uint32)) // NOLINT + return parse_error::kParseInvalidArguments; + *static_cast<Uint32 *>(data) = effect->GetStreamCount(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::GetStreamDesc( + ResourceId id, + unsigned int index, + unsigned int size, + void *data) { + EffectD3D9 *effect = effects_.Get(id); + if (!effect) return parse_error::kParseInvalidArguments; + return effect->GetStreamDesc(index, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + + +// If the current effect is valid, call End on it, and tag for revalidation. +void GAPID3D9::DirtyEffect() { + if (validate_effect_) return; + DCHECK(current_effect_); + current_effect_->End(); + current_effect_ = NULL; + validate_effect_ = true; +} + +// Gets the current effect, and calls Begin on it (if successful). +// Should only be called if the current effect is not valid. +bool GAPID3D9::ValidateEffect() { + DCHECK(validate_effect_); + DCHECK(!current_effect_); + current_effect_ = effects_.Get(current_effect_id_); + if (!current_effect_) return false; + validate_effect_ = false; + return current_effect_->Begin(); +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/effect_d3d9.h b/o3d/gpu/command_buffer/service/effect_d3d9.h new file mode 100644 index 0000000..db2df41 --- /dev/null +++ b/o3d/gpu/command_buffer/service/effect_d3d9.h @@ -0,0 +1,139 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the D3D9 versions of effect-related +// resource classes. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_EFFECT_D3D9_H_ +#define GPU_COMMAND_BUFFER_SERVICE_EFFECT_D3D9_H_ + +#include <vector> +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/resource.h" + +namespace command_buffer { +namespace o3d { + +class GAPID3D9; +class EffectD3D9; + +// ps_2_0 limit +static const unsigned int kMaxSamplerUnits = 16; + +// D3D version of EffectParam. This class keeps a reference to the D3D effect. +class EffectParamD3D9: public EffectParam { + public: + EffectParamD3D9(effect_param::DataType data_type, + EffectD3D9 *effect, + D3DXHANDLE handle); + virtual ~EffectParamD3D9(); + + // Sets the data into the D3D effect parameter. + bool SetData(GAPID3D9 *gapi, unsigned int size, const void * data); + + // Gets the description of the parameter. + bool GetDesc(unsigned int size, void *data); + + // Resets the effect back-pointer. This is called when the effect gets + // destroyed, to invalidate the parameter. + void ResetEffect() { effect_ = NULL; } + + static EffectParamD3D9 *Create(EffectD3D9 *effect, D3DXHANDLE handle); + private: + EffectD3D9 *effect_; + D3DXHANDLE handle_; + unsigned int sampler_unit_count_; + scoped_array<unsigned int> sampler_units_; +}; + +// D3D9 version of Effect. +class EffectD3D9 : public Effect { + public: + EffectD3D9(GAPID3D9 *gapi, + ID3DXEffect *d3d_effect, + ID3DXConstantTable *fs_constant_table, + IDirect3DVertexShader9 *d3d_vertex_shader); + virtual ~EffectD3D9(); + // Compiles and creates an effect from source code. + static EffectD3D9 *Create(GAPID3D9 *gapi, + const String &effect_code, + const String &vertex_program_entry, + const String &fragment_program_entry); + // Applies the effect states (vertex shader, pixel shader) to D3D. + bool Begin(); + // Resets the effect states (vertex shader, pixel shader) to D3D. + void End(); + // Commits parameters to D3D, if they were modified while the effect is + // active. + bool CommitParameters(); + + // Gets the number of parameters in the effect. + unsigned int GetParamCount(); + // Creates an effect parameter with the specified index. + EffectParamD3D9 *CreateParam(unsigned int index); + // Creates an effect parameter of the specified name. + EffectParamD3D9 *CreateParamByName(const char *name); + // Gets the number of stream inputs for the effect. + unsigned int GetStreamCount(); + // Gets the stream data with the specified index. + bool GetStreamDesc(unsigned int index, unsigned int size, void *data); + private: + typedef std::vector<EffectParamD3D9 *> ParamList; + typedef std::vector<effect_stream::Desc> StreamList; + + // Links a param into this effect. + void LinkParam(EffectParamD3D9 *param); + // Unlinks a param into this effect. + void UnlinkParam(EffectParamD3D9 *param); + // Sets sampler states. + bool SetSamplers(); + // Sets streams vector. + bool SetStreams(); + + GAPID3D9* gapi_; + ID3DXEffect *d3d_effect_; + IDirect3DVertexShader9 *d3d_vertex_shader_; + ID3DXConstantTable *fs_constant_table_; + ParamList params_; + StreamList streams_; + bool sync_parameters_; + ResourceId samplers_[kMaxSamplerUnits]; + + friend class EffectParamD3D9; + DISALLOW_COPY_AND_ASSIGN(EffectD3D9); +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_EFFECT_D3D9_H_ diff --git a/o3d/gpu/command_buffer/service/effect_gl.cc b/o3d/gpu/command_buffer/service/effect_gl.cc new file mode 100644 index 0000000..b8f9601 --- /dev/null +++ b/o3d/gpu/command_buffer/service/effect_gl.cc @@ -0,0 +1,852 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the EffectParamGL and EffectGL +// classes, as well as the effect-related GAPI functions on GL. + +#include "gpu/command_buffer/service/precompile.h" + +#include <map> + +#include "base/cross/std_functional.h" +#include "gpu/command_buffer/service/effect_gl.h" +#include "gpu/command_buffer/service/gapi_gl.h" +#include "gpu/command_buffer/service/effect_utils.h" + +namespace command_buffer { +namespace o3d { + +EffectParamGL::EffectParamGL(effect_param::DataType data_type, + EffectGL *effect, + unsigned int param_index) + : EffectParam(data_type), + effect_(effect), + low_level_param_index_(param_index) { + DCHECK(effect_); + effect_->LinkParam(this); +} + +EffectParamGL::~EffectParamGL() { + if (effect_) + effect_->UnlinkParam(this); +} + +static effect_param::DataType CgTypeToCBType(CGtype cg_type) { + switch (cg_type) { + case CG_FLOAT: + case CG_FLOAT1: + return effect_param::kFloat1; + case CG_FLOAT2: + return effect_param::kFloat2; + case CG_FLOAT3: + return effect_param::kFloat3; + case CG_FLOAT4: + return effect_param::kFloat4; + case CG_INT: + case CG_INT1: + return effect_param::kInt; + case CG_BOOL: + case CG_BOOL1: + return effect_param::kBool; + case CG_FLOAT4x4: + return effect_param::kMatrix4; + case CG_SAMPLER: + case CG_SAMPLER1D: + case CG_SAMPLER2D: + case CG_SAMPLER3D: + case CG_SAMPLERCUBE: + return effect_param::kSampler; + case CG_TEXTURE: + return effect_param::kTexture; + default : { + DLOG(INFO) << "Cannot convert CGtype " + << cgGetTypeString(cg_type) + << " to a Param type."; + return effect_param::kUnknown; + } + } +} + +EffectParamGL *EffectParamGL::Create(EffectGL *effect, + unsigned int index) { + DCHECK(effect); + const EffectGL::LowLevelParam &low_level_param = + effect->low_level_params_[index]; + CGparameter cg_param = EffectGL::GetEitherCgParameter(low_level_param); + CGtype cg_type = cgGetParameterType(cg_param); + if (cg_type == CG_ARRAY) { + cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); + } + effect_param::DataType type = CgTypeToCBType(cg_type); + + if (type == effect_param::kUnknown) + return NULL; + return new EffectParamGL(type, effect, index); +} + +// Fills the Desc structure, appending name and semantic if any, and if enough +// room is available in the buffer. +bool EffectParamGL::GetDesc(unsigned int size, void *data) { + using effect_param::Desc; + if (size < sizeof(Desc)) // NOLINT + return false; + if (!effect_) + return false; + const EffectGL::LowLevelParam &low_level_param = + effect_->low_level_params_[low_level_param_index_]; + CGparameter cg_param = EffectGL::GetEitherCgParameter(low_level_param); + const char *name = low_level_param.name; + const char* semantic = cgGetParameterSemantic(cg_param); + int num_elements = cgGetArraySize(cg_param, 0); + unsigned int name_size = + name ? static_cast<unsigned int>(strlen(name)) + 1 : 0; + unsigned int semantic_size = semantic ? + static_cast<unsigned int>(strlen(semantic)) + 1 : 0; + unsigned int total_size = sizeof(Desc) + name_size + semantic_size; // NOLINT + + Desc *desc = static_cast<Desc *>(data); + memset(desc, 0, sizeof(*desc)); + desc->size = total_size; + desc->data_type = data_type(); + desc->data_size = GetDataSize(data_type()); + desc->name_offset = 0; + desc->name_size = name_size; + desc->semantic_offset = 0; + desc->num_elements = num_elements; + desc->semantic_size = semantic_size; + unsigned int current_offset = sizeof(Desc); + if (name && current_offset + name_size <= size) { + desc->name_offset = current_offset; + memcpy(static_cast<char *>(data) + current_offset, name, name_size); + current_offset += name_size; + } + if (semantic && current_offset + semantic_size <= size) { + desc->semantic_offset = current_offset; + memcpy(static_cast<char *>(data) + current_offset, semantic, semantic_size); + current_offset += semantic_size; + } + return true; +} + +// Sets the data into the Cg effect parameter, using the appropriate Cg call. +bool EffectParamGL::SetData(GAPIGL *gapi, + unsigned int size, + const void * data) { + if (!effect_) + return false; + + EffectGL::LowLevelParam &low_level_param = + effect_->low_level_params_[low_level_param_index_]; + + if (low_level_param.num_elements != 0) { + DLOG(ERROR) << "Attempt to set array parameter to value."; + return false; + } + + CGparameter vp_param = low_level_param.vp_param; + CGparameter fp_param = low_level_param.fp_param; + effect_param::DataType type = data_type(); + if (size < effect_param::GetDataSize(type)) + return false; + + switch (type) { + case effect_param::kFloat1: + if (vp_param) + cgSetParameter1f(vp_param, *static_cast<const float *>(data)); + if (fp_param) + cgSetParameter1f(fp_param, *static_cast<const float *>(data)); + break; + case effect_param::kFloat2: + if (vp_param) + cgSetParameter2fv(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetParameter2fv(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kFloat3: + if (vp_param) + cgSetParameter3fv(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetParameter3fv(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kFloat4: + if (vp_param) + cgSetParameter4fv(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetParameter4fv(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kMatrix4: + if (vp_param) + cgSetMatrixParameterfr(vp_param, static_cast<const float *>(data)); + if (fp_param) + cgSetMatrixParameterfr(fp_param, static_cast<const float *>(data)); + break; + case effect_param::kInt: + if (vp_param) cgSetParameter1i(vp_param, *static_cast<const int *>(data)); + if (fp_param) cgSetParameter1i(fp_param, *static_cast<const int *>(data)); + break; + case effect_param::kBool: { + int bool_value = *static_cast<const bool *>(data)?1:0; + if (vp_param) cgSetParameter1i(vp_param, bool_value); + if (fp_param) cgSetParameter1i(fp_param, bool_value); + break; + } + case effect_param::kSampler: { + DCHECK_GE(low_level_param.sampler_ids.size(), 1U); + low_level_param.sampler_ids[0] = *static_cast<const ResourceId *>(data); + if (effect_ == gapi->current_effect()) { + gapi->DirtyEffect(); + } + break; + } + default: + DLOG(ERROR) << "Invalid parameter type."; + return false; + } + return true; +} +EffectGL::EffectGL(CGprogram vertex_program, + CGprogram fragment_program) + : vertex_program_(vertex_program), + fragment_program_(fragment_program), + update_samplers_(true) { +} + +EffectGL::~EffectGL() { + for (ParamList::iterator it = params_.begin(); + it != params_.end(); ++it) { + (*it)->ResetEffect(); + } +} + +void EffectGL::LinkParam(EffectParamGL *param) { + params_.push_back(param); +} + +void EffectGL::UnlinkParam(EffectParamGL *param) { + std::remove(params_.begin(), params_.end(), param); +} + +// Rewrites vertex program assembly code to match GL semantics for clipping. +// This parses the source, breaking it down into pieces: +// - declaration ("!!ARBvp1.0") +// - comments (that contain the parameter information) +// - instructions +// - "END" token. +// Then it rewrites the instructions so that 'result.position' doesn't get +// written directly, instead it is written to a temporary variable. Then a +// transformation is done on that variable before outputing to +// 'result.position': +// - offset x an y by half a pixel (times w). +// - remap z from [0..w] to [-w..w]. +// +// Note that for the 1/2 pixel offset, we need a parameter that depends on the +// current viewport. This is done through 'program.env[0]' which is shared +// across all programs (so we only have to update it once when we change the +// viewport), because Cg won't use them currently (it uses 'program.local' +// instead). +static bool RewriteVertexProgramSource(String *source) { + String::size_type pos = source->find('\n'); + if (pos == String::npos) { + DLOG(ERROR) << "could not find program declaration"; + return false; + } + String decl(*source, 0, pos + 1); + String::size_type start_comments = pos + 1; + // skip the comments that contain the parameters etc. + for (; pos < source->size(); pos = source->find('\n', pos)) { + ++pos; + if (pos >= source->size()) + break; + if ((*source)[pos] != '#') + break; + } + if (pos >= source->size()) { + // we only found comments. + return false; + } + String comments(*source, start_comments, pos - start_comments); + + String::size_type end_token = source->find("\nEND", pos + 1); + if (end_token == String::npos) { + DLOG(ERROR) << "Compiled shader doesn't have an END token"; + return false; + } + String instructions(*source, pos, end_token + 1 - pos); + + // Replace accesses to 'result.position' by accesses to our temp variable + // '$O3D_HPOS'. + // '$' is a valid symbol for identifiers, but Cg doesn't seem to be using + // it, so we can use it to ensure we don't have name conflicts. + static const char kOutPositionRegister[] = "result.position"; + for (String::size_type i = instructions.find(kOutPositionRegister); + i < String::npos; i = instructions.find(kOutPositionRegister, i)) { + instructions.replace(i, strlen(kOutPositionRegister), "$O3D_HPOS"); + } + + *source = decl + + comments + + // .x = 1/viewport.width; .y = 1/viewport.height; .z = 2.0; + "PARAM $O3D_HELPER = program.env[0];\n" + "TEMP $O3D_HPOS;\n" + + instructions + + // hpos.x <- hpos.x + hpos.w / viewport.width; + // hpos.y <- hpos.y - hpos.w / viewport.height; + "MAD $O3D_HPOS.xy, $O3D_HELPER.xyyy, $O3D_HPOS.w, $O3D_HPOS.xyyy;\n" + // hpos.z <- hpos.z * 2 - hpos.w + "MAD $O3D_HPOS.z, $O3D_HPOS.z, $O3D_HELPER.z, -$O3D_HPOS.w;\n" + "MOV result.position, $O3D_HPOS;\n" + "END\n"; + return true; +} + +EffectGL *EffectGL::Create(GAPIGL *gapi, + const String& effect_code, + const String& vertex_program_entry, + const String& fragment_program_entry) { + CGcontext context = gapi->cg_context(); + // Compile the original vertex program once, to get the ARBVP1 assembly code. + CGprogram original_vp = cgCreateProgram( + context, CG_SOURCE, effect_code.c_str(), CG_PROFILE_ARBVP1, + vertex_program_entry.c_str(), NULL); + const char* listing = cgGetLastListing(context); + if (original_vp == NULL) { + DLOG(ERROR) << "Effect Compile Error: " << cgGetErrorString(cgGetError()) + << " : " << listing; + return NULL; + } + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect Compile Warnings: " << listing; + } + + String vp_assembly = cgGetProgramString(original_vp, CG_COMPILED_PROGRAM); + cgDestroyProgram(original_vp); + if (!RewriteVertexProgramSource(&vp_assembly)) { + return NULL; + } + CGprogram vertex_program = cgCreateProgram( + context, CG_OBJECT, vp_assembly.c_str(), CG_PROFILE_ARBVP1, + vertex_program_entry.c_str(), NULL); + listing = cgGetLastListing(context); + if (vertex_program == NULL) { + DLOG(ERROR) << "Effect post-rewrite Compile Error: " + << cgGetErrorString(cgGetError()) << " : " << listing; + return false; + } + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect post-rewrite compile warnings: " << listing; + } + + CHECK_GL_ERROR(); + + // If the program rewrite introduced some syntax or semantic errors, we won't + // know it until we load the program (through a GL error). + // So flush all GL errors first... + do {} while (glGetError() != GL_NO_ERROR); + + // ... Then load the program ... + cgGLLoadProgram(vertex_program); + + // ... And check for GL errors. + if (glGetError() != GL_NO_ERROR) { + DLOG(ERROR) << "Effect post-rewrite GL Error: " + << glGetString(GL_PROGRAM_ERROR_STRING_ARB) + << "\nSource: \n" + << vp_assembly; + return NULL; + } + + CGprogram fragment_program = cgCreateProgram( + context, CG_SOURCE, effect_code.c_str(), CG_PROFILE_ARBFP1, + fragment_program_entry.c_str(), NULL); + listing = cgGetLastListing(context); + if (fragment_program == NULL) { + DLOG(ERROR) << "Effect Compile Error: " + << cgGetErrorString(cgGetError()) << " : " + << listing; + return NULL; + } + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect Compile Warnings: " << listing; + } + + cgGLLoadProgram(fragment_program); + + // Also check for GL errors, in case Cg managed to compile, but generated a + // bad program. + if (glGetError() != GL_NO_ERROR) { + DLOG(ERROR) << "Effect GL Error: " + << glGetString(GL_PROGRAM_ERROR_STRING_ARB); + return false; + } + EffectGL *effect = new EffectGL(vertex_program, fragment_program); + effect->Initialize(); + return effect; +} + +int EffectGL::GetLowLevelParamIndexByName(const char *name) { + DCHECK(name); + for (unsigned int index = 0; index < low_level_params_.size(); ++index) { + if (!strcmp(name, low_level_params_[index].name)) { + return index; + } + } + return -1; +} + +void EffectGL::AddLowLevelParams(CGprogram prog, CGenum name_space, bool vp) { + // Iterate through parameters and add them to the vector of low level + // parameters, visiting only CGparameters that have had storage allocated to + // them, and add the params to the low_level_params_ vector. + for (CGparameter cg_param = cgGetFirstParameter(prog, name_space); + cg_param != NULL; + cg_param = cgGetNextParameter(cg_param)) { + CGenum variability = cgGetParameterVariability(cg_param); + if (variability != CG_UNIFORM) + continue; + CGenum direction = cgGetParameterDirection(cg_param); + if (direction != CG_IN) + continue; + const char *name = cgGetParameterName(cg_param); + if (!name) + continue; + + CGtype cg_type = cgGetParameterType(cg_param); + + int num_elements; + if (cg_type == CG_ARRAY) { + num_elements = cgGetArraySize(cg_param, 0); + // Substitute the first element's type for our type. + cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); + } else { + num_elements = 0; + } + + int index = GetLowLevelParamIndexByName(name); + if (index < 0) { + LowLevelParam param; + param.name = name; + param.vp_param = NULL; + param.fp_param = NULL; + param.num_elements = num_elements; + + index = low_level_params_.size(); + if (cg_type == CG_SAMPLER || + cg_type == CG_SAMPLER1D || + cg_type == CG_SAMPLER2D || + cg_type == CG_SAMPLER3D || + cg_type == CG_SAMPLERCUBE) { + sampler_params_.push_back(index); + if (num_elements == 0) { + param.sampler_ids.push_back(kInvalidResource); + } else { + param.sampler_ids.resize(num_elements); + std::vector<ResourceId>::iterator iter; + for (iter = param.sampler_ids.begin(); + iter != param.sampler_ids.end(); + ++iter) { + *iter = kInvalidResource; + } + } + } + low_level_params_.push_back(param); + } + + if (vp) { + low_level_params_[index].vp_param = cg_param; + } else { + low_level_params_[index].fp_param = cg_param; + } + } +} + +typedef std::pair<String, effect_stream::Desc> SemanticMapElement; +typedef std::map<String, effect_stream::Desc> SemanticMap; + +// The map batween the semantics on vertex program varying parameters names +// and vertex attribute indices under the VP_30 profile. +// TODO(gman): remove this. +SemanticMapElement semantic_map_array[] = { + SemanticMapElement("POSITION", + effect_stream::Desc(vertex_struct::kPosition, 0)), + SemanticMapElement("ATTR0", + effect_stream::Desc(vertex_struct::kPosition, 0)), + SemanticMapElement("BLENDWEIGHT", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR1", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("NORMAL", + effect_stream::Desc(vertex_struct::kNormal, 0)), + SemanticMapElement("ATTR2", + effect_stream::Desc(vertex_struct::kNormal, 0)), + SemanticMapElement("COLOR0", + effect_stream::Desc(vertex_struct::kColor, 0)), + SemanticMapElement("DIFFUSE", + effect_stream::Desc(vertex_struct::kColor, 0)), + SemanticMapElement("ATTR3", + effect_stream::Desc(vertex_struct::kColor, 0)), + SemanticMapElement("COLOR1", + effect_stream::Desc(vertex_struct::kColor, 1)), + SemanticMapElement("SPECULAR", + effect_stream::Desc(vertex_struct::kColor, 1)), + SemanticMapElement("ATTR4", + effect_stream::Desc(vertex_struct::kColor, 1)), + SemanticMapElement("TESSFACTOR", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("FOGCOORD", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR5", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("PSIZE", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR6", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("BLENDINDICES", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("ATTR7", + effect_stream::Desc(vertex_struct::kUnknownSemantic, 0)), + SemanticMapElement("TEXCOORD0", + effect_stream::Desc(vertex_struct::kTexCoord, 0)), + SemanticMapElement("ATTR8", + effect_stream::Desc(vertex_struct::kTexCoord, 0)), + SemanticMapElement("TEXCOORD1", + effect_stream::Desc(vertex_struct::kTexCoord, 1)), + SemanticMapElement("ATTR9", + effect_stream::Desc(vertex_struct::kTexCoord, 1)), + SemanticMapElement("TEXCOORD2", + effect_stream::Desc(vertex_struct::kTexCoord, 2)), + SemanticMapElement("ATTR10", + effect_stream::Desc(vertex_struct::kTexCoord, 2)), + SemanticMapElement("TEXCOORD3", + effect_stream::Desc(vertex_struct::kTexCoord, 3)), + SemanticMapElement("ATTR11", + effect_stream::Desc(vertex_struct::kTexCoord, 3)), + SemanticMapElement("TEXCOORD4", + effect_stream::Desc(vertex_struct::kTexCoord, 4)), + SemanticMapElement("ATTR12", + effect_stream::Desc(vertex_struct::kTexCoord, 4)), + SemanticMapElement("TEXCOORD5", + effect_stream::Desc(vertex_struct::kTexCoord, 5)), + SemanticMapElement("ATTR13", + effect_stream::Desc(vertex_struct::kTexCoord, 5)), + SemanticMapElement("TEXCOORD6", + effect_stream::Desc(vertex_struct::kTexCoord, 6)), + SemanticMapElement("TANGENT", + effect_stream::Desc(vertex_struct::kTexCoord, 6)), + SemanticMapElement("ATTR14", + effect_stream::Desc(vertex_struct::kTexCoord, 7)), + SemanticMapElement("TEXCOORD7", + effect_stream::Desc(vertex_struct::kTexCoord, 7)), + SemanticMapElement("BINORMAL", + effect_stream::Desc(vertex_struct::kTexCoord, 8)), + SemanticMapElement("ATTR15", + effect_stream::Desc(vertex_struct::kTexCoord, 8)) +}; + +static SemanticMap semantic_map(semantic_map_array, + semantic_map_array + + arraysize(semantic_map_array)); + +void EffectGL::Initialize() { + AddLowLevelParams(vertex_program_, CG_PROGRAM, true); + AddLowLevelParams(vertex_program_, CG_GLOBAL, true); + AddLowLevelParams(fragment_program_, CG_PROGRAM, false); + AddLowLevelParams(fragment_program_, CG_GLOBAL, false); + + AddStreams(vertex_program_, CG_PROGRAM); + AddStreams(vertex_program_, CG_GLOBAL); +} + +// Loop over all leaf parameters, and find the ones that are bound to a +// semantic. +void EffectGL::AddStreams(CGprogram prog, CGenum name_space) { + for (CGparameter cg_param = cgGetFirstLeafParameter(prog, name_space); + cg_param != NULL; + cg_param = cgGetNextLeafParameter(cg_param)) { + CGenum variability = cgGetParameterVariability(cg_param); + if (variability != CG_VARYING) + continue; + CGenum direction = cgGetParameterDirection(cg_param); + if (direction != CG_IN) + continue; + const char* cg_semantic = cgGetParameterSemantic(cg_param); + if (cg_semantic == NULL) + continue; + + SemanticMap::iterator iter = semantic_map.find(String(cg_semantic)); + if (iter == semantic_map.end()) { + streams_.push_back(effect_stream::Desc( + vertex_struct::kUnknownSemantic, 0)); + } else { + streams_.push_back(iter->second); + } + } +} + +// Begins rendering with the effect, setting all the appropriate states. +bool EffectGL::Begin(GAPIGL *gapi) { + cgGLBindProgram(vertex_program_); + cgGLBindProgram(fragment_program_); + + // sampler->ApplyStates will mess with the texture binding on unit 0, so we + // do 2 passes. + // First to set the sampler states on the texture + for (unsigned int i = 0; i < sampler_params_.size(); ++i) { + unsigned int param_index = sampler_params_[i]; + std::vector<ResourceId> &ids = low_level_params_[param_index].sampler_ids; + for (std::vector<ResourceId>::iterator iter = ids.begin(); + iter != ids.end(); + ++iter) { + ResourceId id = *iter; + if (id != kInvalidResource) { + SamplerGL *sampler = gapi->GetSampler(id); + if (!sampler->ApplyStates(gapi)) { + return false; + } + } + } + } + // Second to enable/disable the sampler params. + for (unsigned int i = 0; i < sampler_params_.size(); ++i) { + unsigned int param_index = sampler_params_[i]; + const LowLevelParam &ll_param = low_level_params_[param_index]; + std::vector<ResourceId> &ids = low_level_params_[param_index].sampler_ids; + // TODO(petersont): Rewrite the following so it handles arrays of samplers + // instead of simply bailing. + if (cgGetParameterType(ll_param.fp_param) == CG_ARRAY) + return false; + for (std::vector<ResourceId>::iterator iter = ids.begin(); + iter != ids.end(); + ++iter) { + ResourceId id = *iter; + if (id != kInvalidResource) { + SamplerGL *sampler = gapi->GetSampler(id); + GLuint gl_texture = sampler->gl_texture(); + cgGLSetTextureParameter(ll_param.fp_param, gl_texture); + cgGLEnableTextureParameter(ll_param.fp_param); + } else { + cgGLSetTextureParameter(ll_param.fp_param, 0); + cgGLDisableTextureParameter(ll_param.fp_param); + } + } + } + return true; +} + +// Terminates rendering with the effect, resetting all the appropriate states. +void EffectGL::End(GAPIGL *gapi) { +} + +// Gets the parameter count from the list. +unsigned int EffectGL::GetParamCount() const { + return low_level_params_.size(); +} + +// Gets the number of input streams from the shader. +unsigned int EffectGL::GetStreamCount() const { + return streams_.size(); +} + +// Gets a handle to the selected parameter, and wraps it into an +// EffectParamGL if successful. +EffectParamGL *EffectGL::CreateParam(unsigned int index) { + if (index >= GetParamCount()) + return NULL; + return EffectParamGL::Create(this, index); +} + +// Provided enough room is available in the buffer, fills the Desc structure, +// appending name and semantic if any. +bool EffectGL::GetStreamDesc(unsigned int index, + unsigned int size, + void *data) { + using effect_stream::Desc; + if (size < sizeof(Desc) || index >= streams_.size()) // NOLINT + return false; + + Desc *desc = static_cast<Desc *>(data); + *desc = streams_[index]; + return true; +} + +// Gets a handle to the selected parameter, and wraps it into an +// EffectParamGL if successful. +EffectParamGL *EffectGL::CreateParamByName(const char *name) { + int index = GetLowLevelParamIndexByName(name); + if (index < 0) return NULL; + return EffectParamGL::Create(this, index); +} + +parse_error::ParseError GAPIGL::CreateEffect(ResourceId id, + unsigned int size, + const void *data) { + if (id == current_effect_id_) DirtyEffect(); + // Even though Assign would Destroy the effect at id, we do it explicitly in + // case the creation fails. + effects_.Destroy(id); + // Data is vp_main \0 fp_main \0 effect_text. + String vertex_program_entry; + String fragment_program_entry; + String effect_code; + if (!ParseEffectData(size, data, + &vertex_program_entry, + &fragment_program_entry, + &effect_code)) { + return parse_error::kParseInvalidArguments; + } + EffectGL * effect = EffectGL::Create(this, effect_code, + vertex_program_entry, + fragment_program_entry); + if (!effect) return parse_error::kParseInvalidArguments; + effects_.Assign(id, effect); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyEffect(ResourceId id) { + if (id == current_effect_id_) DirtyEffect(); + return effects_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetEffect(ResourceId id) { + DirtyEffect(); + current_effect_id_ = id; + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::GetParamCount(ResourceId id, + unsigned int size, + void *data) { + EffectGL *effect = effects_.Get(id); + if (!effect || size < sizeof(Uint32)) // NOLINT + return parse_error::kParseInvalidArguments; + *static_cast<Uint32 *>(data) = effect->GetParamCount(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::CreateParam(ResourceId param_id, + ResourceId effect_id, + unsigned int index) { + EffectGL *effect = effects_.Get(effect_id); + if (!effect) return parse_error::kParseInvalidArguments; + EffectParamGL *param = effect->CreateParam(index); + if (!param) return parse_error::kParseInvalidArguments; + effect_params_.Assign(param_id, param); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::CreateParamByName(ResourceId param_id, + ResourceId effect_id, + unsigned int size, + const void *name) { + EffectGL *effect = effects_.Get(effect_id); + if (!effect) return parse_error::kParseInvalidArguments; + std::string string_name(static_cast<const char *>(name), size); + EffectParamGL *param = effect->CreateParamByName(string_name.c_str()); + if (!param) return parse_error::kParseInvalidArguments; + effect_params_.Assign(param_id, param); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyParam(ResourceId id) { + return effect_params_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetParamData(ResourceId id, + unsigned int size, + const void *data) { + EffectParamGL *param = effect_params_.Get(id); + if (!param) return parse_error::kParseInvalidArguments; + return param->SetData(this, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::GetParamDesc(ResourceId id, + unsigned int size, + void *data) { + EffectParamGL *param = effect_params_.Get(id); + if (!param) return parse_error::kParseInvalidArguments; + return param->GetDesc(size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::GetStreamCount( + ResourceId id, + unsigned int size, + void *data) { + EffectGL *effect = effects_.Get(id); + if (!effect || size < sizeof(Uint32)) // NOLINT + return parse_error::kParseInvalidArguments; + *static_cast<Uint32 *>(data) = effect->GetStreamCount(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::GetStreamDesc(ResourceId id, + unsigned int index, + unsigned int size, + void *data) { + EffectGL *effect = effects_.Get(id); + if (!effect) return parse_error::kParseInvalidArguments; + return effect->GetStreamDesc(index, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// If the current effect is valid, call End on it, and tag for revalidation. +void GAPIGL::DirtyEffect() { + if (validate_effect_) return; + DCHECK(current_effect_); + current_effect_->End(this); + current_effect_ = NULL; + validate_effect_ = true; +} + +// Gets the current effect, and calls Begin on it (if successful). +// Should only be called if the current effect is not valid. +bool GAPIGL::ValidateEffect() { + DCHECK(validate_effect_); + DCHECK(!current_effect_); + current_effect_ = effects_.Get(current_effect_id_); + if (!current_effect_) return false; + validate_effect_ = false; + return current_effect_->Begin(this); +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/effect_gl.h b/o3d/gpu/command_buffer/service/effect_gl.h new file mode 100644 index 0000000..b38cc92 --- /dev/null +++ b/o3d/gpu/command_buffer/service/effect_gl.h @@ -0,0 +1,162 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of the EffectParamGL and EffectGL classes. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_EFFECT_GL_H_ +#define GPU_COMMAND_BUFFER_SERVICE_EFFECT_GL_H_ + +#include <vector> + +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/resource.h" +#include "gpu/command_buffer/service/gl_utils.h" + +namespace command_buffer { +namespace o3d { + +class GAPIGL; +class EffectGL; + +// GL version of EffectParam. +class EffectParamGL: public EffectParam { + public: + EffectParamGL(effect_param::DataType data_type, + EffectGL *effect, + unsigned int param_index); + virtual ~EffectParamGL(); + + // Sets the data into the GL effect parameter. + bool SetData(GAPIGL *gapi, unsigned int size, const void * data); + + // Gets the description of the parameter. + bool GetDesc(unsigned int size, void *data); + + // Resets the effect back-pointer. This is called when the effect gets + // destroyed, to invalidate the parameter. + void ResetEffect() { effect_ = NULL; } + + // Creates an EffectParamGL from the EffectGL, by index. + static EffectParamGL *Create(EffectGL *effect, + unsigned int index); + private: + EffectGL *effect_; + unsigned int low_level_param_index_; + DISALLOW_COPY_AND_ASSIGN(EffectParamGL); +}; + +// GL version of Effect. +class EffectGL : public Effect { + public: + EffectGL(CGprogram vertex_program, + CGprogram fragment_program); + virtual ~EffectGL(); + + // Compiles and creates an effect from source code. + static EffectGL *Create(GAPIGL *gapi, + const String &effect_code, + const String &vertex_program_entry, + const String &fragment_program_entry); + + // Applies the effect states (vertex shader, pixel shader) to GL. + bool Begin(GAPIGL *gapi); + + // Resets the effect states (vertex shader, pixel shader) to GL. + void End(GAPIGL *gapi); + + // Gets the number of parameters in the effect. + unsigned int GetParamCount() const; + + // Gets the number of streams in the effect. + unsigned int GetStreamCount() const; + + // Creates an effect parameter with the specified index. + EffectParamGL *CreateParam(unsigned int index); + + // Gets the stream data with the specified index. + bool GetStreamDesc(unsigned int index, unsigned int size, void *data); + + // Creates an effect parameter of the specified name. + EffectParamGL *CreateParamByName(const char *name); + + private: + struct LowLevelParam { + const char *name; + CGparameter vp_param; + CGparameter fp_param; + int num_elements; + std::vector<ResourceId> sampler_ids; + }; + typedef std::vector<LowLevelParam> LowLevelParamList; + typedef std::vector<EffectParamGL *> ParamList; + typedef std::vector<effect_stream::Desc> StreamList; + + static CGparameter GetEitherCgParameter( + const LowLevelParam &low_level_param) { + return low_level_param.vp_param ? + low_level_param.vp_param : low_level_param.fp_param; + } + + int GetLowLevelParamIndexByName(const char *name); + void AddLowLevelParams(CGprogram prog, CGenum name_space, bool vp); + void AddStreams(CGprogram prog, CGenum name_space); + + // Creates the low level structures. + void Initialize(); + + // Links a param into this effect. + void LinkParam(EffectParamGL *param); + + // Unlinks a param into this effect. + void UnlinkParam(EffectParamGL *param); + + CGprogram vertex_program_; + CGprogram fragment_program_; + // List of all the Params created. + ParamList params_; + StreamList streams_; + // List of all the Cg parameters present in either the vertex program or the + // fragment program. + LowLevelParamList low_level_params_; + + // List of the indices of the low level params that are samplers. + std::vector<unsigned int> sampler_params_; + bool update_samplers_; + + friend class EffectParamGL; + DISALLOW_COPY_AND_ASSIGN(EffectGL); +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_EFFECT_GL_H_ diff --git a/o3d/gpu/command_buffer/service/effect_utils.cc b/o3d/gpu/command_buffer/service/effect_utils.cc new file mode 100644 index 0000000..021b49f --- /dev/null +++ b/o3d/gpu/command_buffer/service/effect_utils.cc @@ -0,0 +1,69 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements effect related utilities. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/effect_utils.h" + +namespace command_buffer { + +bool ParseEffectData(unsigned int size, + const void *data, + String *vertex_program_entry, + String *fragment_program_entry, + String *effect_code) { + const char *data_char = static_cast<const char *>(data); + unsigned int index = 0; + + for (; index < size && data_char[index]; ++index) { } + if (index >= size) return false; + *vertex_program_entry = String(data_char, index); + ++index; // skip \0 + unsigned int fragment_program_entry_begin = index; + + for (; index < size && data_char[index]; ++index) { } + if (index >= size) return false; + *fragment_program_entry = String(data_char + fragment_program_entry_begin, + index - fragment_program_entry_begin); + ++index; // skip \0 + unsigned int effect_code_begin = index; + + // text doesn't have to be 0-terminated, but look for one so that we don't + // construct a std::string with a '\0' in it. + for (; index < size && data_char[index]; ++index) { } + *effect_code = String(data_char + effect_code_begin, + index - effect_code_begin); + return true; +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/effect_utils.h b/o3d/gpu/command_buffer/service/effect_utils.h new file mode 100644 index 0000000..ae22fb9 --- /dev/null +++ b/o3d/gpu/command_buffer/service/effect_utils.h @@ -0,0 +1,54 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file declares some effect related utilities. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_EFFECT_UTILS_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_EFFECT_UTILS_H_ + +#include "gpu/command_buffer/common/types.h" + +namespace command_buffer { + +// This function parses the data passed to the CreateEffect commands, which +// follows the following format: +// vertex_program_entry \0 fragment_program_entry \0 effect_code +// It returns the various components. +bool ParseEffectData(unsigned int size, + const void *data, + String *vertex_program_entry, + String *fragment_program_entry, + String *effect_code); + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_EFFECT_UTILS_H_ diff --git a/o3d/gpu/command_buffer/service/effect_utils_test.cc b/o3d/gpu/command_buffer/service/effect_utils_test.cc new file mode 100644 index 0000000..695291e --- /dev/null +++ b/o3d/gpu/command_buffer/service/effect_utils_test.cc @@ -0,0 +1,87 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the unit tests for the effect utilities. + +#include "gpu/command_buffer/service/precompile.h" +#include "tests/common/win/testing_common.h" +#include "gpu/command_buffer/service/cross/effect_utils.h" + +namespace o3d { +namespace command_buffer { + +TEST(ParseEffectDataTest, ValidData) { + // Tests well-formed data. + const char kEffect[] = "vertex_entry\0fragment_entry\0effect code"; + String vertex_program_entry; + String fragment_program_entry; + String effect_code; + EXPECT_TRUE(ParseEffectData(sizeof(kEffect), kEffect, &vertex_program_entry, + &fragment_program_entry, &effect_code)); + EXPECT_EQ(vertex_program_entry, "vertex_entry"); + EXPECT_EQ(fragment_program_entry, "fragment_entry"); + EXPECT_EQ(effect_code, "effect code"); + + // The terminal \0 isn't needed, check that we parse correctly without it. + EXPECT_TRUE(ParseEffectData(sizeof(kEffect)-1, kEffect, + &vertex_program_entry, &fragment_program_entry, + &effect_code)); + EXPECT_EQ(vertex_program_entry, "vertex_entry"); + EXPECT_EQ(fragment_program_entry, "fragment_entry"); + EXPECT_EQ(effect_code, "effect code"); +} + +TEST(ParseEffectDataTest, InvalidData) { + const char kEffect[] = "vertex_entry\0fragment_entry\0effect code"; + String vertex_program_entry; + String fragment_program_entry; + String effect_code; + // 0-size + EXPECT_FALSE(ParseEffectData(0, kEffect, + &vertex_program_entry, &fragment_program_entry, + &effect_code)); + // Only vertex_entry, no \0 + EXPECT_FALSE(ParseEffectData(strlen("vertex_entry"), kEffect, + &vertex_program_entry, &fragment_program_entry, + &effect_code)); + // Only vertex_entry\0 + EXPECT_FALSE(ParseEffectData(strlen("vertex_entry") + 1, kEffect, + &vertex_program_entry, &fragment_program_entry, + &effect_code)); + // Only vertex_entry\0fragment_entry, no \0 + EXPECT_FALSE(ParseEffectData(strlen("vertex_entry.fragment_entry"), kEffect, + &vertex_program_entry, &fragment_program_entry, + &effect_code)); +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/gpu/command_buffer/service/gapi_d3d9.cc b/o3d/gpu/command_buffer/service/gapi_d3d9.cc new file mode 100644 index 0000000..8ca8cfd --- /dev/null +++ b/o3d/gpu/command_buffer/service/gapi_d3d9.cc @@ -0,0 +1,393 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the GAPID3D9 class. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/gapi_d3d9.h" + +namespace command_buffer { +namespace o3d { + +GAPID3D9::GAPID3D9() + : d3d_module_(NULL), + d3dx_module_(NULL), + d3d_(NULL), + d3d_device_(NULL), + hwnd_(NULL), + current_vertex_struct_(0), + validate_streams_(true), + max_vertices_(0), + current_effect_id_(0), + validate_effect_(true), + current_effect_(NULL), + vertex_buffers_(), + index_buffers_(), + vertex_structs_(), + back_buffer_surface_(NULL), + back_buffer_depth_surface_(NULL), + current_surface_id_(kInvalidResource), + current_depth_surface_id_(kInvalidResource), + direct3d_create9_(NULL), + get_shader_constant_table_(NULL), + create_effect_(NULL), + get_shader_input_semantics_(NULL) {} + +GAPID3D9::~GAPID3D9() {} + +// Initializes a D3D interface and device, and sets basic states. +bool GAPID3D9::Initialize() { + if (!FindDirect3DFunctions()) { + Destroy(); + return false; + } + + d3d_ = Direct3DCreate(D3D_SDK_VERSION); + if (NULL == d3d_) { + LOG(ERROR) << "Failed to create the initial D3D9 Interface"; + Destroy(); + return false; + } + d3d_device_ = NULL; + + D3DDISPLAYMODE d3ddm; + d3d_->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); + // NOTE: make sure the backbuffer matches this format, as it is + // currently currently assumed to be 32-bit 8X8R8G8B + + D3DPRESENT_PARAMETERS d3dpp; + ZeroMemory(&d3dpp, sizeof(d3dpp)); + d3dpp.Windowed = TRUE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.BackBufferFormat = d3ddm.Format; + d3dpp.EnableAutoDepthStencil = TRUE; + d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // wait for vsync + // Note: SwapEffect=DISCARD is req. for multisample to function + // Note: AutoDepthStencilFormat is 16-bit (not the usual 8-bit) + + // query multisampling + const int kNumTypesToCheck = 4; + D3DMULTISAMPLE_TYPE multisample_types[] = { D3DMULTISAMPLE_5_SAMPLES, + D3DMULTISAMPLE_4_SAMPLES, + D3DMULTISAMPLE_2_SAMPLES, + D3DMULTISAMPLE_NONE }; + DWORD multisample_quality = 0; + for (int i = 0; i < kNumTypesToCheck; ++i) { + // check back-buffer for multisampling at level "i"; + // back buffer = 32-bit XRGB (i.e. no alpha) + if (SUCCEEDED(d3d_->CheckDeviceMultiSampleType( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + D3DFMT_X8R8G8B8, + true, // result is windowed + multisample_types[i], + &multisample_quality))) { + // back buffer succeeded, now check depth-buffer + // depth buffer = 24-bit, stencil = 8-bit + // NOTE: 8-bit not 16-bit like the D3DPRESENT_PARAMETERS + if (SUCCEEDED(d3d_->CheckDeviceMultiSampleType( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + D3DFMT_D24S8, + true, // result is windowed + multisample_types[i], + &multisample_quality))) { + d3dpp.MultiSampleType = multisample_types[i]; + d3dpp.MultiSampleQuality = multisample_quality - 1; + break; + } + } + } + // D3DCREATE_FPU_PRESERVE is there because Firefox 3 relies on specific FPU + // flags for its UI rendering. Apparently Firefox 2 does not, though we don't + // currently propagate that info. + // TODO: check if FPU_PRESERVE has a significant perf hit, in which + // case find out if we can disable it for Firefox 2/other browsers, and/or if + // it makes sense to switch FPU flags before/after every DX call. + DWORD flags = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE; + if (!SUCCEEDED(d3d_->CreateDevice(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + hwnd_, + flags, + &d3dpp, + &d3d_device_))) { + LOG(ERROR) << "Failed to create the D3D Device"; + Destroy(); + return false; + } + // initialise the d3d graphics state. + HR(d3d_device_->SetRenderState(D3DRS_LIGHTING, FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_ZENABLE, TRUE)); + HR(d3d_device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE)); + return true; +} + +// Deletes the D3D9 Device and releases the D3D interface. +void GAPID3D9::Destroy() { + vertex_buffers_.DestroyAllResources(); + index_buffers_.DestroyAllResources(); + vertex_structs_.DestroyAllResources(); + effects_.DestroyAllResources(); + effect_params_.DestroyAllResources(); + textures_.DestroyAllResources(); + samplers_.DestroyAllResources(); + render_surfaces_.DestroyAllResources(); + depth_surfaces_.DestroyAllResources(); + if (d3d_device_) { + d3d_device_->Release(); + d3d_device_ = NULL; + } + if (d3d_) { + d3d_->Release(); + d3d_ = NULL; + } + if (d3dx_module_) { + FreeLibrary(d3dx_module_); + d3dx_module_ = NULL; + get_shader_constant_table_ = NULL; + create_effect_ = NULL; + get_shader_input_semantics_ = NULL; + } + if (d3d_module_) { + FreeLibrary(d3d_module_); + d3d_module_ = NULL; + direct3d_create9_ = NULL; + } +} + +// Begins the frame. +void GAPID3D9::BeginFrame() { + HR(d3d_device_->GetRenderTarget(0, &back_buffer_surface_)); + HR(d3d_device_->GetDepthStencilSurface(&back_buffer_depth_surface_)); + HR(d3d_device_->BeginScene()); +} + +// Ends the frame, presenting the back buffer. +void GAPID3D9::EndFrame() { + DirtyEffect(); + HR(d3d_device_->EndScene()); + HR(d3d_device_->Present(NULL, NULL, NULL, NULL)); + + // Release the back-buffer references. + back_buffer_surface_ = NULL; + back_buffer_depth_surface_ = NULL; +} + +// Clears the selected buffers. +void GAPID3D9::Clear(unsigned int buffers, + const RGBA &color, + float depth, + unsigned int stencil) { + DWORD flags = (buffers & kColor ? D3DCLEAR_TARGET : 0) | + (buffers & kDepth ? D3DCLEAR_ZBUFFER : 0) | + (buffers & kStencil ? D3DCLEAR_STENCIL : 0); + HR(d3d_device_->Clear(0, + NULL, + flags, + D3DCOLOR_COLORVALUE(color.red, + color.green, + color.blue, + color.alpha), + depth, + stencil)); +} + +// Sets the viewport. +void GAPID3D9::SetViewport(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height, + float z_min, + float z_max) { + D3DVIEWPORT9 viewport = {x, y, width, height, z_min, z_max}; + HR(d3d_device_->SetViewport(&viewport)); +} + +// Converts an unsigned int RGBA color into an unsigned int ARGB (DirectX) +// color. +static unsigned int RGBAToARGB(unsigned int rgba) { + return (rgba >> 8) | (rgba << 24); +} + +// Sets the current VertexStruct. Just keep track of the ID. +parse_error::ParseError GAPID3D9::SetVertexStruct(ResourceId id) { + current_vertex_struct_ = id; + validate_streams_ = true; + return parse_error::kParseNoError; +} + +bool GAPID3D9::FindDirect3DFunctions() { + d3d_module_ = LoadLibrary(TEXT("d3d9.dll")); + if (NULL == d3d_module_) { + LOG(ERROR) << "Failed to load d3d9.dll"; + return false; + } + + direct3d_create9_ = reinterpret_cast<Direct3DCreate9Proc>( + GetProcAddress(d3d_module_, "Direct3DCreate9")); + if (NULL == direct3d_create9_) { + LOG(ERROR) << "Failed to find Direct3DCreate9 in d3d9.dll"; + Destroy(); + return false; + } + + d3dx_module_ = LoadLibrary(TEXT("d3dx9_36.dll")); + if (NULL == d3d_module_) { + LOG(ERROR) << "Failed to load d3dx9_36.dll"; + return false; + } + + get_shader_constant_table_ = reinterpret_cast<D3DXGetShaderConstantTableProc>( + GetProcAddress(d3dx_module_, "D3DXGetShaderConstantTable")); + if (NULL == get_shader_constant_table_) { + LOG(ERROR) << "Failed to find D3DXGetShaderConstantTable in d3dx9_36.dll"; + Destroy(); + return false; + } + + create_effect_ = reinterpret_cast<D3DXCreateEffectProc>( + GetProcAddress(d3dx_module_, "D3DXCreateEffect")); + if (NULL == create_effect_) { + LOG(ERROR) << "Failed to find D3DXCreateEffect in d3dx9_36.dll"; + Destroy(); + return false; + } + + get_shader_input_semantics_ = + reinterpret_cast<D3DXGetShaderInputSemanticsProc>( + GetProcAddress(d3dx_module_, "D3DXGetShaderInputSemantics")); + if (NULL == get_shader_input_semantics_) { + LOG(ERROR) << "Failed to find D3DXGetShaderInputSemantics in d3dx9_36.dll"; + Destroy(); + return false; + } + + return true; +} + +// Sets in D3D the input streams of the current vertex struct. +bool GAPID3D9::ValidateStreams() { + DCHECK(validate_streams_); + VertexStructD3D9 *vertex_struct = vertex_structs_.Get(current_vertex_struct_); + if (!vertex_struct) { + LOG(ERROR) << "Drawing with invalid streams."; + return false; + } + max_vertices_ = vertex_struct->SetStreams(this); + validate_streams_ = false; + return max_vertices_ > 0; +} + +// Converts a GAPID3D9::PrimitiveType to a D3DPRIMITIVETYPE. +static D3DPRIMITIVETYPE D3DPrimitive( + PrimitiveType primitive_type) { + switch (primitive_type) { + case kPoints: + return D3DPT_POINTLIST; + case kLines: + return D3DPT_LINELIST; + case kLineStrips: + return D3DPT_LINESTRIP; + case kTriangles: + return D3DPT_TRIANGLELIST; + case kTriangleStrips: + return D3DPT_TRIANGLESTRIP; + case kTriangleFans: + return D3DPT_TRIANGLEFAN; + default: + LOG(FATAL) << "Invalid primitive type"; + return D3DPT_POINTLIST; + } +} + +// Draws with the current vertex struct. +parse_error::ParseError GAPID3D9::Draw( + PrimitiveType primitive_type, + unsigned int first, + unsigned int count) { + if (validate_streams_ && !ValidateStreams()) { + // TODO: add proper error management + return parse_error::kParseInvalidArguments; + } + if (validate_effect_ && !ValidateEffect()) { + // TODO: add proper error management + return parse_error::kParseInvalidArguments; + } + DCHECK(current_effect_); + if (!current_effect_->CommitParameters()) { + return parse_error::kParseInvalidArguments; + } + if (first + count > max_vertices_) { + // TODO: add proper error management + return parse_error::kParseInvalidArguments; + } + HR(d3d_device_->DrawPrimitive(D3DPrimitive(primitive_type), first, count)); + return parse_error::kParseNoError; +} + +// Draws with the current vertex struct. +parse_error::ParseError GAPID3D9::DrawIndexed( + PrimitiveType primitive_type, + ResourceId index_buffer_id, + unsigned int first, + unsigned int count, + unsigned int min_index, + unsigned int max_index) { + IndexBufferD3D9 *index_buffer = index_buffers_.Get(index_buffer_id); + if (!index_buffer) return parse_error::kParseInvalidArguments; + if (validate_streams_ && !ValidateStreams()) { + // TODO: add proper error management + return parse_error::kParseInvalidArguments; + } + if (validate_effect_ && !ValidateEffect()) { + // TODO: add proper error management + return parse_error::kParseInvalidArguments; + } + DCHECK(current_effect_); + if (!current_effect_->CommitParameters()) { + return parse_error::kParseInvalidArguments; + } + if ((min_index >= max_vertices_) || (max_index > max_vertices_)) { + // TODO: add proper error management + return parse_error::kParseInvalidArguments; + } + + HR(d3d_device_->SetIndices(index_buffer->d3d_index_buffer())); + HR(d3d_device_->DrawIndexedPrimitive(D3DPrimitive(primitive_type), 0, + min_index, max_index - min_index + 1, + first, count)); + return parse_error::kParseNoError; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/gapi_d3d9.h b/o3d/gpu/command_buffer/service/gapi_d3d9.h new file mode 100644 index 0000000..1056dc1 --- /dev/null +++ b/o3d/gpu/command_buffer/service/gapi_d3d9.h @@ -0,0 +1,508 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the GAPID3D9 class, implementing the GAPI interface for +// D3D9. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_GAPI_D3D9_H_ +#define GPU_COMMAND_BUFFER_SERVICE_GAPI_D3D9_H_ + +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/geometry_d3d9.h" +#include "gpu/command_buffer/service/effect_d3d9.h" +#include "gpu/command_buffer/service/texture_d3d9.h" +#include "gpu/command_buffer/service/sampler_d3d9.h" +#include "gpu/command_buffer/service/render_surface_d3d9.h" + +namespace command_buffer { +namespace o3d { + +// This class implements the GAPI interface for D3D9. +class GAPID3D9 : public GAPIInterface { + public: + GAPID3D9(); + virtual ~GAPID3D9(); + + void set_hwnd(HWND hwnd) { hwnd_ = hwnd; } + HWND hwnd() const { return hwnd_; } + + // Initializes the graphics context, bound to a window. + // Returns: + // true if successful. + virtual bool Initialize(); + + // Destroys the graphics context. + virtual void Destroy(); + + // Implements the BeginFrame function for D3D9. + virtual void BeginFrame(); + + // Implements the EndFrame function for D3D9. + virtual void EndFrame(); + + // Implements the Clear function for D3D9. + virtual void Clear(unsigned int buffers, + const RGBA &color, + float depth, + unsigned int stencil); + + // Implements the SetViewport function for D3D9. + virtual void SetViewport(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height, + float z_min, + float z_max); + + // Implements the CreateVertexBuffer function for D3D9. + virtual ParseError CreateVertexBuffer(ResourceId id, + unsigned int size, + unsigned int flags); + + // Implements the DestroyVertexBuffer function for D3D9. + virtual ParseError DestroyVertexBuffer(ResourceId id); + + // Implements the SetVertexBufferData function for D3D9. + virtual ParseError SetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data); + + // Implements the GetVertexBufferData function for D3D9. + virtual ParseError GetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data); + + // Implements the CreateIndexBuffer function for D3D9. + virtual ParseError CreateIndexBuffer(ResourceId id, + unsigned int size, + unsigned int flags); + + // Implements the DestroyIndexBuffer function for D3D9. + virtual ParseError DestroyIndexBuffer(ResourceId id); + + // Implements the SetIndexBufferData function for D3D9. + virtual ParseError SetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data); + + // Implements the GetIndexBufferData function for D3D9. + virtual ParseError GetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data); + + // Implements the CreateVertexStruct function for D3D9. + virtual ParseError CreateVertexStruct(ResourceId id, + unsigned int input_count); + + // Implements the DestroyVertexStruct function for D3D9. + virtual ParseError DestroyVertexStruct(ResourceId id); + + // Implements the SetVertexInput function for D3D9. + virtual ParseError SetVertexInput(ResourceId vertex_struct_id, + unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index); + + // Implements the SetVertexStruct function for D3D9. + virtual ParseError SetVertexStruct(ResourceId id); + + // Implements the Draw function for D3D9. + virtual ParseError Draw(PrimitiveType primitive_type, + unsigned int first, + unsigned int count); + + // Implements the DrawIndexed function for D3D9. + virtual ParseError DrawIndexed(PrimitiveType primitive_type, + ResourceId index_buffer_id, + unsigned int first, + unsigned int count, + unsigned int min_index, + unsigned int max_index); + + // Implements the CreateEffect function for D3D9. + virtual ParseError CreateEffect(ResourceId id, + unsigned int size, + const void *data); + + // Implements the DestroyEffect function for D3D9. + virtual ParseError DestroyEffect(ResourceId id); + + // Implements the SetEffect function for D3D9. + virtual ParseError SetEffect(ResourceId id); + + // Implements the GetParamCount function for D3D9. + virtual ParseError GetParamCount(ResourceId id, + unsigned int size, + void *data); + + // Implements the CreateParam function for D3D9. + virtual ParseError CreateParam(ResourceId param_id, + ResourceId effect_id, + unsigned int index); + + // Implements the CreateParamByName function for D3D9. + virtual ParseError CreateParamByName(ResourceId param_id, + ResourceId effect_id, + unsigned int size, + const void *name); + + // Implements the DestroyParam function for D3D9. + virtual ParseError DestroyParam(ResourceId id); + + // Implements the SetParamData function for D3D9. + virtual ParseError SetParamData(ResourceId id, + unsigned int size, + const void *data); + + // Implements the GetParamDesc function for D3D9. + virtual ParseError GetParamDesc(ResourceId id, + unsigned int size, + void *data); + + // Implements the GetStreamCount function for D3D9. + virtual ParseError GetStreamCount(ResourceId id, + unsigned int size, + void *data); + + // Implements the GetStreamDesc function for D3D9. + virtual ParseError GetStreamDesc(ResourceId id, + unsigned int index, + unsigned int size, + void *data); + + // Implements the CreateTexture2D function for D3D9. + virtual ParseError CreateTexture2D(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Implements the CreateTexture3D function for D3D9. + virtual ParseError CreateTexture3D(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Implements the CreateTextureCube function for D3D9. + virtual ParseError CreateTextureCube(ResourceId id, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Implements the SetTextureData function for D3D9. + virtual ParseError SetTextureData(ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + + // Implements the GetTextureData function for D3D9. + virtual ParseError GetTextureData(ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + + // Implements the DestroyTexture function for D3D9. + virtual ParseError DestroyTexture(ResourceId id); + + // Implements the CreateSampler function for D3D9. + virtual ParseError CreateSampler(ResourceId id); + + // Implements the DestroySampler function for D3D9. + virtual ParseError DestroySampler(ResourceId id); + + // Implements the SetSamplerStates function for D3D9. + virtual ParseError SetSamplerStates(ResourceId id, + sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy); + + // Implements the SetSamplerBorderColor function for D3D9. + virtual ParseError SetSamplerBorderColor(ResourceId id, const RGBA &color); + + // Implements the SetSamplerTexture function for D3D9. + virtual ParseError SetSamplerTexture(ResourceId id, ResourceId texture_id); + + // Implements the SetScissor function for D3D9. + virtual void SetScissor(bool enable, + unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height); + + // Implements the SetPointLineRaster function for D3D9. + virtual void SetPointLineRaster(bool line_smooth, + bool point_sprite, + float point_size); + + // Implements the SetPolygonOffset function for D3D9. + virtual void SetPolygonOffset(float slope_factor, float units); + + // Implements the SetPolygonRaster function for D3D9. + virtual void SetPolygonRaster(PolygonMode fill_mode, + FaceCullMode cull_mode); + + // Implements the SetAlphaTest function for D3D9. + virtual void SetAlphaTest(bool enable, + float reference, + Comparison comp); + + // Implements the SetDepthTest function for D3D9. + virtual void SetDepthTest(bool enable, + bool write_enable, + Comparison comp); + + // Implements the SetStencilTest function for D3D9. + virtual void SetStencilTest(bool enable, + bool separate_ccw, + unsigned int write_mask, + unsigned int compare_mask, + unsigned int ref, + Uint32 func_ops); + + // Implements the SetColorWritefunction for D3D9. + virtual void SetColorWrite(bool red, + bool green, + bool blue, + bool alpha, + bool dither); + + // Implements the SetBlending function for D3D9. + virtual void SetBlending(bool enable, + bool separate_alpha, + BlendEq color_eq, + BlendFunc color_src_func, + BlendFunc color_dst_func, + BlendEq alpha_eq, + BlendFunc alpha_src_func, + BlendFunc alpha_dst_func); + + // Implements the SetBlendingColor function for D3D9. + virtual void SetBlendingColor(const RGBA &color); + + // Implements the CreateRenderSurface function for D3D9. + virtual ParseError CreateRenderSurface(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int mip_level, + unsigned int side, + ResourceId texture_id); + + // Implements the DestroyRenderSurface function for D3D9. + virtual ParseError DestroyRenderSurface(ResourceId id); + + // Implements the CreateDepthSurface function for D3D9. + virtual ParseError CreateDepthSurface(ResourceId id, + unsigned int width, + unsigned int height); + + // Implements teh DestroyDepthSurface function for D3D9. + virtual ParseError DestroyDepthSurface(ResourceId id); + + // Implements the SetRenderSurface function for D3D9. + virtual ParseError SetRenderSurface(ResourceId render_surface_id, + ResourceId depth_stencil_id); + + // Implements the SetBackSurfaces function for D3D9. + virtual void SetBackSurfaces(); + + // Gets the D3D9 device. + IDirect3DDevice9 *d3d_device() const { return d3d_device_; } + + // Gets a vertex buffer by resource ID. + VertexBufferD3D9 *GetVertexBuffer(ResourceId id) { + return vertex_buffers_.Get(id); + } + + // Gets a texture by resource ID. + TextureD3D9 *GetTexture(ResourceId id) { + return textures_.Get(id); + } + + // Gets a sampler by resource ID. + SamplerD3D9 *GetSampler(ResourceId id) { + return samplers_.Get(id); + } + + EffectD3D9 *current_effect() { return current_effect_; } + + // Direct3D functions cannot be called directly because the DLLs are loaded + // dynamically via LoadLibrary. If you need to add another Direct3D function + // add another function here, a typedef matching the signature and a member + // variable of that type below. Then add code to FindDirect3DFunctions to + // get the address of that function out of the DLL and assign it to the + // member variable. Be careful to initialize the value of the variable to + // NULL in the constructor and to set it to again NULL in Destroy. + + IDirect3D9* Direct3DCreate(UINT version) { + DCHECK(direct3d_create9_); + return direct3d_create9_(version); + } + + HRESULT D3DXGetShaderConstantTable(const DWORD* function, + LPD3DXCONSTANTTABLE* table) { + DCHECK(get_shader_constant_table_); + return get_shader_constant_table_(function, table); + } + + HRESULT D3DXCreateEffect(LPDIRECT3DDEVICE9 device, + LPCVOID src_data, + UINT src_data_len, + CONST D3DXMACRO * defines, + LPD3DXINCLUDE include, + DWORD flags, + LPD3DXEFFECTPOOL pool, + LPD3DXEFFECT * effect, + LPD3DXBUFFER * compilation_errors) { + DCHECK(create_effect_); + return create_effect_(device, src_data, src_data_len, defines, include, + flags, pool, effect, compilation_errors); + } + + HRESULT D3DXGetShaderInputSemantics(const DWORD* function, + D3DXSEMANTIC* semantics, + UINT* count) { + DCHECK(get_shader_input_semantics_); + return get_shader_input_semantics_(function, semantics, count); + } + + private: + bool FindDirect3DFunctions(); + + // Validates the current vertex struct to D3D, setting the streams. + bool ValidateStreams(); + // Validates the current effect to D3D. This sends the effect states to D3D. + bool ValidateEffect(); + // "Dirty" the current effect. This resets the effect states to D3D, and + // requires ValidateEffect() to be called before further draws occur. + void DirtyEffect(); + + // Module handle for d3d9.dll. + HMODULE d3d_module_; + + // Module handle for d3dx9_n.dll + HMODULE d3dx_module_; + + LPDIRECT3D9 d3d_; + LPDIRECT3DDEVICE9 d3d_device_; + HWND hwnd_; + ResourceId current_vertex_struct_; + bool validate_streams_; + unsigned int max_vertices_; + ResourceId current_effect_id_; + bool validate_effect_; + EffectD3D9 *current_effect_; + IDirect3DSurface9* back_buffer_surface_; + IDirect3DSurface9* back_buffer_depth_surface_; + ResourceId current_surface_id_; + ResourceId current_depth_surface_id_; + + ResourceMap<VertexBufferD3D9> vertex_buffers_; + ResourceMap<IndexBufferD3D9> index_buffers_; + ResourceMap<VertexStructD3D9> vertex_structs_; + ResourceMap<EffectD3D9> effects_; + ResourceMap<EffectParamD3D9> effect_params_; + ResourceMap<TextureD3D9> textures_; + ResourceMap<SamplerD3D9> samplers_; + ResourceMap<RenderSurfaceD3D9> render_surfaces_; + ResourceMap<RenderDepthStencilSurfaceD3D9> depth_surfaces_; + + typedef IDirect3D9* (WINAPI *Direct3DCreate9Proc)(UINT version); + Direct3DCreate9Proc direct3d_create9_; + + typedef HRESULT (WINAPI *D3DXGetShaderConstantTableProc)( + const DWORD* function, + LPD3DXCONSTANTTABLE* table); + D3DXGetShaderConstantTableProc get_shader_constant_table_; + + typedef HRESULT (WINAPI *D3DXCreateEffectProc)( + LPDIRECT3DDEVICE9 device, + LPCVOID src_data, + UINT src_data_len, + CONST D3DXMACRO * defines, + LPD3DXINCLUDE include, + DWORD flags, + LPD3DXEFFECTPOOL pool, + LPD3DXEFFECT * effect, + LPD3DXBUFFER * compilation_errors); + D3DXCreateEffectProc create_effect_; + + typedef HRESULT (WINAPI *D3DXGetShaderInputSemanticsProc)( + const DWORD* function, + D3DXSEMANTIC* semantics, + UINT* count); + D3DXGetShaderInputSemanticsProc get_shader_input_semantics_; +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_GAPI_D3D9_H_ + diff --git a/o3d/gpu/command_buffer/service/gapi_decoder.cc b/o3d/gpu/command_buffer/service/gapi_decoder.cc new file mode 100644 index 0000000..4d91b69 --- /dev/null +++ b/o3d/gpu/command_buffer/service/gapi_decoder.cc @@ -0,0 +1,940 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This class contains the implementation of the GAPI decoder class, decoding +// GAPI commands into calls to a GAPIInterface class. + +#include "gpu/command_buffer/service/precompile.h" + +#include "base/cross/bits.h" +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/gapi_decoder.h" + +namespace command_buffer { +namespace o3d { + +namespace { + +// Returns the address of the first byte after a struct. +template <typename T> +const void* AddressAfterStruct(const T& pod) { + return reinterpret_cast<const uint8*>(&pod) + sizeof(pod); +} + +// Returns the size in bytes of the data of an Immediate command, a command with +// its data inline in the command buffer. +template <typename T> +unsigned int ImmediateDataSize(uint32 arg_count, const T& pod) { + return static_cast<unsigned int>( + (arg_count + 1 - ComputeNumEntries(sizeof(pod))) * + sizeof(CommandBufferEntry)); // NOLINT +} + +// A struct to hold info about each command. +struct CommandInfo { + int arg_flags; // How to handle the arguments for this command + int arg_count; // How many arguments are expected for this command. +}; + +// A table of CommandInfo for all the commands. +const CommandInfo g_command_info[] = { + #define O3D_COMMAND_BUFFER_CMD_OP(name) { \ + name::kArgFlags, \ + sizeof(name) / sizeof(CommandBufferEntry) - 1, }, /* NOLINT */ \ + + O3D_COMMAND_BUFFER_CMDS(O3D_COMMAND_BUFFER_CMD_OP) + + #undef O3D_COMMAND_BUFFER_CMD_OP +}; + +} // anonymous namespace. + +// Decode command with its arguments, and call the corresponding method. +// Note: args is a pointer to the command buffer. As such, it could be changed +// by a (malicious) client at any time, so if validation has to happen, it +// should operate on a copy of them. +parse_error::ParseError GAPIDecoder::DoCommand( + unsigned int command, + unsigned int arg_count, + const void* cmd_data) { + unsigned int command_index = command - kStartPoint - 1; + if (command_index < arraysize(g_command_info)) { + const CommandInfo& info = g_command_info[command_index]; + unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count); + if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) || + (info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) { + switch (command) { + #define O3D_COMMAND_BUFFER_CMD_OP(name) \ + case name::kCmdId: \ + return Handle ## name( \ + arg_count, \ + *static_cast<const name*>(cmd_data)); \ + + O3D_COMMAND_BUFFER_CMDS(O3D_COMMAND_BUFFER_CMD_OP) + + #undef O3D_COMMAND_BUFFER_CMD_OP + } + } else { + return parse_error::kParseInvalidArguments; + } + } + return DoCommonCommand(command, arg_count, cmd_data); + return parse_error::kParseUnknownCommand; +} + + // Overridden from AsyncAPIInterface. +const char* GAPIDecoder::GetCommandName(unsigned int command_id) const { + if (command_id > kStartPoint && command_id < kNumCommands) { + return o3d::GetCommandName(static_cast<CommandId>(command_id)); + } + return GetCommonCommandName(static_cast<cmd::CommandId>(command_id)); +} + +parse_error::ParseError GAPIDecoder::HandleBeginFrame( + uint32 arg_count, + const BeginFrame& args) { + gapi_->BeginFrame(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleEndFrame( + uint32 arg_count, + const EndFrame& args) { + gapi_->EndFrame(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleClear( + uint32 arg_count, + const Clear& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 buffers = args.buffers; + if (buffers & ~kAllBuffers) + return parse_error::kParseInvalidArguments; + RGBA rgba; + rgba.red = args.red; + rgba.green = args.green; + rgba.blue = args.blue; + rgba.alpha = args.alpha; + gapi_->Clear(buffers, rgba, args.depth, args.stencil); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetViewport( + uint32 arg_count, + const SetViewport& args) { + gapi_->SetViewport(args.left, + args.top, + args.width, + args.height, + args.z_min, + args.z_max); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleCreateVertexBuffer( + uint32 arg_count, + const CreateVertexBuffer& args) { + return gapi_->CreateVertexBuffer( + args.vertex_buffer_id, args.size, args.flags); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyVertexBuffer( + uint32 arg_count, + const DestroyVertexBuffer& args) { + return gapi_->DestroyVertexBuffer(args.vertex_buffer_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetVertexBufferDataImmediate( + uint32 arg_count, + const SetVertexBufferDataImmediate& args) { + uint32 size = ImmediateDataSize(arg_count, args); + if (size == 0) { + return parse_error::kParseNoError; + } + return gapi_->SetVertexBufferData(args.vertex_buffer_id, args.offset, + size, + AddressAfterStruct(args)); +} + +parse_error::ParseError GAPIDecoder::HandleSetVertexBufferData( + uint32 arg_count, + const SetVertexBufferData& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->SetVertexBufferData( + args.vertex_buffer_id, args.offset, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleGetVertexBufferData( + uint32 arg_count, + const GetVertexBufferData& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->GetVertexBufferData( + args.vertex_buffer_id, args.offset, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleCreateIndexBuffer( + uint32 arg_count, + const CreateIndexBuffer& args) { + return gapi_->CreateIndexBuffer(args.index_buffer_id, args.size, args.flags); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyIndexBuffer( + uint32 arg_count, + const DestroyIndexBuffer& args) { + return gapi_->DestroyIndexBuffer(args.index_buffer_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetIndexBufferDataImmediate( + uint32 arg_count, + const SetIndexBufferDataImmediate& args) { + uint32 size = ImmediateDataSize(arg_count, args); + if (size == 0) { + return parse_error::kParseNoError; + } + return gapi_->SetIndexBufferData(args.index_buffer_id, args.offset, size, + AddressAfterStruct(args)); +} + +parse_error::ParseError GAPIDecoder::HandleSetIndexBufferData( + uint32 arg_count, + const SetIndexBufferData& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->SetIndexBufferData( + args.index_buffer_id, args.offset, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleGetIndexBufferData( + uint32 arg_count, + const GetIndexBufferData& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->GetIndexBufferData( + args.index_buffer_id, args.offset, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleCreateVertexStruct( + uint32 arg_count, + const CreateVertexStruct& args) { + return gapi_->CreateVertexStruct(args.vertex_struct_id, args.input_count); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyVertexStruct( + uint32 arg_count, + const DestroyVertexStruct& args) { + return gapi_->DestroyVertexStruct(args.vertex_struct_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetVertexInput( + uint32 arg_count, + const SetVertexInput& args) { + unsigned int type_stride_semantic = args.type_stride_semantic; + unsigned int semantic_index = + SetVertexInput::SemanticIndex::Get(type_stride_semantic); + unsigned int semantic = + SetVertexInput::Semantic::Get(type_stride_semantic); + unsigned int type = + SetVertexInput::Type::Get(type_stride_semantic); + unsigned int stride = + SetVertexInput::Stride::Get(type_stride_semantic); + if (semantic >= vertex_struct::kNumSemantics || + type >= vertex_struct::kNumTypes || stride == 0) + return parse_error::kParseInvalidArguments; + return gapi_->SetVertexInput( + args.vertex_struct_id, args.input_index, args.vertex_buffer_id, + args.offset, stride, + static_cast<vertex_struct::Type>(type), + static_cast<vertex_struct::Semantic>(semantic), + semantic_index); +} + +parse_error::ParseError GAPIDecoder::HandleSetVertexStruct( + uint32 arg_count, + const SetVertexStruct& args) { + return gapi_->SetVertexStruct(args.vertex_struct_id); +} + +parse_error::ParseError GAPIDecoder::HandleDraw( + uint32 arg_count, + const Draw& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 primitive_type = args.primitive_type; + if (primitive_type >= kMaxPrimitiveType) + return parse_error::kParseInvalidArguments; + return gapi_->Draw( + static_cast<PrimitiveType>(primitive_type), + args.first, args.count); +} + +parse_error::ParseError GAPIDecoder::HandleDrawIndexed( + uint32 arg_count, + const DrawIndexed& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 primitive_type = args.primitive_type; + if (primitive_type >= kMaxPrimitiveType) + return parse_error::kParseInvalidArguments; + return gapi_->DrawIndexed( + static_cast<PrimitiveType>(primitive_type), + args.index_buffer_id, + args.first, args.count, args.min_index, args.max_index); +} + +parse_error::ParseError GAPIDecoder::HandleCreateEffect( + uint32 arg_count, + const CreateEffect& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->CreateEffect(args.effect_id, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleCreateEffectImmediate( + uint32 arg_count, + const CreateEffectImmediate& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + uint32 data_size = ImmediateDataSize(arg_count, args); + if (size > data_size) { + return parse_error::kParseInvalidArguments; + } + if (data_size == 0) { + return parse_error::kParseNoError; + } + return gapi_->CreateEffect(args.effect_id, size, AddressAfterStruct(args)); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyEffect( + uint32 arg_count, + const DestroyEffect& args) { + return gapi_->DestroyEffect(args.effect_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetEffect( + uint32 arg_count, + const SetEffect& args) { + return gapi_->SetEffect(args.effect_id); +} + +parse_error::ParseError GAPIDecoder::HandleGetParamCount( + uint32 arg_count, + const GetParamCount& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->GetParamCount(args.effect_id, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleCreateParam( + uint32 arg_count, + const CreateParam& args) { + return gapi_->CreateParam(args.param_id, args.effect_id, args.index); +} + +parse_error::ParseError GAPIDecoder::HandleCreateParamByName( + uint32 arg_count, + const CreateParamByName& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->CreateParamByName(args.param_id, args.effect_id, size, + data); +} + +parse_error::ParseError GAPIDecoder::HandleCreateParamByNameImmediate( + uint32 arg_count, + const CreateParamByNameImmediate& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + uint32 data_size = ImmediateDataSize(arg_count, args); + if (size > data_size) + return parse_error::kParseInvalidArguments; + if (data_size == 0) { + return parse_error::kParseNoError; + } + return gapi_->CreateParamByName(args.param_id, args.effect_id, size, + AddressAfterStruct(args)); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyParam( + uint32 arg_count, + const DestroyParam& args) { + return gapi_->DestroyParam(args.param_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetParamData( + uint32 arg_count, + const SetParamData& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->SetParamData(args.param_id, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleSetParamDataImmediate( + uint32 arg_count, + const SetParamDataImmediate& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + uint32 data_size = ImmediateDataSize(arg_count, args); + if (size > data_size) { + return parse_error::kParseInvalidArguments; + } + if (data_size == 0) { + return parse_error::kParseNoError; + } + return gapi_->SetParamData(args.param_id, size, AddressAfterStruct(args)); +} + +parse_error::ParseError GAPIDecoder::HandleGetParamDesc( + uint32 arg_count, + const GetParamDesc& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->GetParamDesc(args.param_id, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleGetStreamCount( + uint32 arg_count, + const GetStreamCount& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->GetStreamCount(args.effect_id, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleGetStreamDesc( + uint32 arg_count, + const GetStreamDesc& args) { + // Pull out some values so they can't be changed by another thread after we've + // validated them. + uint32 size = args.size; + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, + size); + if (!data) return parse_error::kParseInvalidArguments; + return gapi_->GetStreamDesc(args.effect_id, args.index, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyTexture( + uint32 arg_count, + const DestroyTexture& args) { + return gapi_->DestroyTexture(args.texture_id); +} + +parse_error::ParseError GAPIDecoder::HandleCreateTexture2d( + uint32 arg_count, + const CreateTexture2d& args) { + unsigned int width_height = args.width_height; + unsigned int levels_format_flags = args.levels_format_flags; + unsigned int width = CreateTexture2d::Width::Get(width_height); + unsigned int height = CreateTexture2d::Height::Get(width_height); + unsigned int levels = CreateTexture2d::Levels::Get(levels_format_flags); + unsigned int unused = CreateTexture2d::Unused::Get(levels_format_flags); + unsigned int format = CreateTexture2d::Format::Get(levels_format_flags); + unsigned int flags = CreateTexture2d::Flags::Get(levels_format_flags); + unsigned int max_levels = + 1 + ::o3d::base::bits::Log2Ceiling(std::max(width, height)); + if ((width == 0) || (height == 0) || (levels > max_levels) || + (unused != 0) || (format == texture::kUnknown) || (levels == 0)) + return parse_error::kParseInvalidArguments; + bool enable_render_surfaces = !!flags; + return gapi_->CreateTexture2D(args.texture_id, width, height, levels, + static_cast<texture::Format>(format), flags, + enable_render_surfaces); +} + +parse_error::ParseError GAPIDecoder::HandleCreateTexture3d( + uint32 arg_count, + const CreateTexture3d& args) { + unsigned int width_height = args.width_height; + unsigned int depth_unused = args.depth_unused; + unsigned int levels_format_flags = args.levels_format_flags; + unsigned int width = CreateTexture3d::Width::Get(width_height); + unsigned int height = CreateTexture3d::Height::Get(width_height); + unsigned int depth = CreateTexture3d::Depth::Get(depth_unused); + unsigned int unused1 = CreateTexture3d::Unused1::Get(depth_unused); + unsigned int levels = CreateTexture3d::Levels::Get(levels_format_flags); + unsigned int unused2 = CreateTexture3d::Unused2::Get(levels_format_flags); + unsigned int format = CreateTexture3d::Format::Get(levels_format_flags); + unsigned int flags = CreateTexture3d::Flags::Get(levels_format_flags); + unsigned int max_levels = + 1 + ::o3d::base::bits::Log2Ceiling(std::max(depth, std::max(width, height))); + if ((width == 0) || (height == 0) || (depth == 0) || + (levels > max_levels) || (unused1 != 0) || (unused2 != 0) || + (format == texture::kUnknown) || (levels == 0)) + return parse_error::kParseInvalidArguments; + bool enable_render_surfaces = !!flags; + return gapi_->CreateTexture3D(args.texture_id, width, height, depth, levels, + static_cast<texture::Format>(format), flags, + enable_render_surfaces); +} + +parse_error::ParseError GAPIDecoder::HandleCreateTextureCube( + uint32 arg_count, + const CreateTextureCube& args) { + unsigned int side_unused = args.edge_length; + unsigned int levels_format_flags = args.levels_format_flags; + unsigned int side = CreateTextureCube::Side::Get(side_unused); + unsigned int unused1 = CreateTextureCube::Unused1::Get(side_unused); + unsigned int levels = CreateTextureCube::Levels::Get(levels_format_flags); + unsigned int unused2 = CreateTextureCube::Unused2::Get(levels_format_flags); + unsigned int format = CreateTextureCube::Format::Get(levels_format_flags); + unsigned int flags = CreateTextureCube::Flags::Get(levels_format_flags); + unsigned int max_levels = 1 + ::o3d::base::bits::Log2Ceiling(side); + if ((side == 0) || (levels > max_levels) || (unused1 != 0) || + (unused2 != 0) || (format == texture::kUnknown) || (levels == 0)) + return parse_error::kParseInvalidArguments; + bool enable_render_surfaces = !!flags; + return gapi_->CreateTextureCube(args.texture_id, side, levels, + static_cast<texture::Format>(format), + flags, enable_render_surfaces); +} + +parse_error::ParseError GAPIDecoder::HandleSetTextureData( + uint32 arg_count, + const SetTextureData& args) { + unsigned int x_y = args.x_y; + unsigned int width_height = args.width_height; + unsigned int z_depth = args.z_depth; + unsigned int level_face = args.level_face; + unsigned int size = args.size; + unsigned int x = SetTextureData::X::Get(x_y); + unsigned int y = SetTextureData::Y::Get(x_y); + unsigned int width = SetTextureData::Width::Get(width_height); + unsigned int height = SetTextureData::Height::Get(width_height); + unsigned int z = SetTextureData::Z::Get(z_depth); + unsigned int depth = SetTextureData::Depth::Get(z_depth); + unsigned int level = SetTextureData::Level::Get(level_face); + unsigned int face = SetTextureData::Face::Get(level_face); + unsigned int unused = SetTextureData::Unused::Get(level_face); + const void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, size); + if (face >= 6 || unused != 0 || !data) + return parse_error::kParseInvalidArguments; + return gapi_->SetTextureData( + args.texture_id, x, y, z, width, height, depth, level, + static_cast<texture::Face>(face), args.row_pitch, + args.slice_pitch, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleSetTextureDataImmediate( + uint32 arg_count, + const SetTextureDataImmediate& args) { + unsigned int x_y = args.x_y; + unsigned int width_height = args.width_height; + unsigned int z_depth = args.z_depth; + unsigned int level_face = args.level_face; + unsigned int size = args.size; + unsigned int x = SetTextureDataImmediate::X::Get(x_y); + unsigned int y = SetTextureDataImmediate::Y::Get(x_y); + unsigned int width = SetTextureDataImmediate::Width::Get(width_height); + unsigned int height = SetTextureDataImmediate::Height::Get(width_height); + unsigned int z = SetTextureDataImmediate::Z::Get(z_depth); + unsigned int depth = SetTextureDataImmediate::Depth::Get(z_depth); + unsigned int level = SetTextureDataImmediate::Level::Get(level_face); + unsigned int face = SetTextureDataImmediate::Face::Get(level_face); + unsigned int unused = SetTextureDataImmediate::Unused::Get(level_face); + uint32 data_size = ImmediateDataSize(arg_count, args); + if (face >= 6 || unused != 0 || + size > data_size) + return parse_error::kParseInvalidArguments; + if (data_size == 0) { + return parse_error::kParseNoError; + } + return gapi_->SetTextureData( + args.texture_id, x, y, z, width, height, depth, level, + static_cast<texture::Face>(face), args.row_pitch, + args.slice_pitch, size, AddressAfterStruct(args)); +} + +parse_error::ParseError GAPIDecoder::HandleGetTextureData( + uint32 arg_count, + const GetTextureData& args) { + unsigned int x_y = args.x_y; + unsigned int width_height = args.width_height; + unsigned int z_depth = args.z_depth; + unsigned int level_face = args.level_face; + unsigned int size = args.size; + unsigned int x = GetTextureData::X::Get(x_y); + unsigned int y = GetTextureData::Y::Get(x_y); + unsigned int width = GetTextureData::Width::Get(width_height); + unsigned int height = GetTextureData::Height::Get(width_height); + unsigned int z = GetTextureData::Z::Get(z_depth); + unsigned int depth = GetTextureData::Depth::Get(z_depth); + unsigned int level = GetTextureData::Level::Get(level_face); + unsigned int face = GetTextureData::Face::Get(level_face); + unsigned int unused = GetTextureData::Unused::Get(level_face); + void *data = GetAddressAndCheckSize(args.shared_memory.id, + args.shared_memory.offset, size); + if (face >= 6 || unused != 0 || !data) + return parse_error::kParseInvalidArguments; + return gapi_->GetTextureData( + args.texture_id, x, y, z, width, height, depth, level, + static_cast<texture::Face>(face), args.row_pitch, + args.slice_pitch, size, data); +} + +parse_error::ParseError GAPIDecoder::HandleCreateSampler( + uint32 arg_count, + const CreateSampler& args) { + return gapi_->CreateSampler(args.sampler_id); +} + +parse_error::ParseError GAPIDecoder::HandleDestroySampler( + uint32 arg_count, + const DestroySampler& args) { + return gapi_->DestroySampler(args.sampler_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetSamplerStates( + uint32 arg_count, + const SetSamplerStates& args) { + Uint32 arg = args.sampler_states; + if (SetSamplerStates::Unused::Get(arg) != 0) + return parse_error::kParseInvalidArguments; + unsigned int address_u_value = SetSamplerStates::AddressingU::Get(arg); + unsigned int address_v_value = SetSamplerStates::AddressingV::Get(arg); + unsigned int address_w_value = SetSamplerStates::AddressingW::Get(arg); + unsigned int mag_filter_value = SetSamplerStates::MagFilter::Get(arg); + unsigned int min_filter_value = SetSamplerStates::MinFilter::Get(arg); + unsigned int mip_filter_value = SetSamplerStates::MipFilter::Get(arg); + unsigned int max_anisotropy = SetSamplerStates::MaxAnisotropy::Get(arg); + if (address_u_value >= sampler::kNumAddressingMode || + address_v_value >= sampler::kNumAddressingMode || + address_w_value >= sampler::kNumAddressingMode || + mag_filter_value >= sampler::kNumFilteringMode || + min_filter_value >= sampler::kNumFilteringMode || + mip_filter_value >= sampler::kNumFilteringMode || + mag_filter_value == sampler::kNone || + min_filter_value == sampler::kNone || + max_anisotropy == 0) { + return parse_error::kParseInvalidArguments; + } + gapi_->SetSamplerStates( + args.sampler_id, + static_cast<sampler::AddressingMode>(address_u_value), + static_cast<sampler::AddressingMode>(address_v_value), + static_cast<sampler::AddressingMode>(address_w_value), + static_cast<sampler::FilteringMode>(mag_filter_value), + static_cast<sampler::FilteringMode>(min_filter_value), + static_cast<sampler::FilteringMode>(mip_filter_value), + max_anisotropy); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetSamplerBorderColor( + uint32 arg_count, + const SetSamplerBorderColor& args) { + RGBA rgba; + rgba.red = args.red; + rgba.green = args.green; + rgba.blue = args.blue; + rgba.alpha = args.alpha; + return gapi_->SetSamplerBorderColor(args.sampler_id, rgba); +} + +parse_error::ParseError GAPIDecoder::HandleSetSamplerTexture( + uint32 arg_count, + const SetSamplerTexture& args) { + return gapi_->SetSamplerTexture(args.sampler_id, args.texture_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetScissor( + uint32 arg_count, + const SetScissor& args) { + Uint32 x_y_enable = args.x_y_enable; + if (SetScissor::Unused::Get(x_y_enable) != 0) + return parse_error::kParseInvalidArguments; + unsigned int x = SetScissor::X::Get(x_y_enable); + unsigned int y = SetScissor::Y::Get(x_y_enable); + bool enable = SetScissor::Enable::Get(x_y_enable) != 0; + Uint32 width_height = args.width_height; + unsigned int width = SetScissor::Width::Get(width_height); + unsigned int height = SetScissor::Height::Get(width_height); + gapi_->SetScissor(enable, x, y, width, height); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetPolygonOffset( + uint32 arg_count, + const SetPolygonOffset& args) { + gapi_->SetPolygonOffset(args.slope_factor, args.units); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetPointLineRaster( + uint32 arg_count, + const SetPointLineRaster& args) { + Uint32 enables = args.enables; + if (SetPointLineRaster::Unused::Get(enables) != 0) + return parse_error::kParseInvalidArguments; + bool line_smooth = !!SetPointLineRaster::LineSmoothEnable::Get(enables); + bool point_sprite = !!SetPointLineRaster::PointSpriteEnable::Get(enables); + gapi_->SetPointLineRaster(line_smooth, point_sprite, args.point_size); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetPolygonRaster( + uint32 arg_count, + const SetPolygonRaster& args) { + Uint32 fill_cull = args.fill_cull; + unsigned int fill_value = SetPolygonRaster::FillMode::Get(fill_cull); + unsigned int cull_value = SetPolygonRaster::CullMode::Get(fill_cull); + if (SetPolygonRaster::Unused::Get(fill_cull) != 0 || + fill_value >= kNumPolygonMode || + cull_value >= kNumFaceCullMode) + return parse_error::kParseInvalidArguments; + gapi_->SetPolygonRaster( + static_cast<PolygonMode>(fill_value), + static_cast<FaceCullMode>(cull_value)); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetAlphaTest( + uint32 arg_count, + const SetAlphaTest& args) { + Uint32 func_enable = args.func_enable; + if (SetAlphaTest::Unused::Get(func_enable) != 0) + return parse_error::kParseInvalidArguments; + // Check that the bitmask get cannot generate values outside of the + // allowed range. + COMPILE_ASSERT(SetAlphaTest::Func::kMask < + kNumComparison, + set_alpha_test_Func_may_produce_invalid_values); + Comparison comp = static_cast<Comparison>( + SetAlphaTest::Func::Get(func_enable)); + bool enable = SetAlphaTest::Enable::Get(func_enable) != 0; + gapi_->SetAlphaTest(enable, args.value, comp); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetDepthTest( + uint32 arg_count, + const SetDepthTest& args) { + Uint32 func_enable = args.func_enable; + if (SetDepthTest::Unused::Get(func_enable) != 0) + return parse_error::kParseInvalidArguments; + // Check that the bitmask get cannot generate values outside of the + // allowed range. + COMPILE_ASSERT(SetDepthTest::Func::kMask < + kNumComparison, + set_alpha_test_Func_may_produce_invalid_values); + Comparison comp = static_cast<Comparison>( + SetDepthTest::Func::Get(func_enable)); + bool write_enable = SetDepthTest::WriteEnable::Get(func_enable) != 0; + bool enable = SetDepthTest::Enable::Get(func_enable) != 0; + gapi_->SetDepthTest(enable, write_enable, comp); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetStencilTest( + uint32 arg_count, + const SetStencilTest& args) { + Uint32 arg0 = args.stencil_args0; + Uint32 arg1 = args.stencil_args1; + if (SetStencilTest::Unused0::Get(arg0) != 0 || + SetStencilTest::Unused1::Get(arg1) != 0 || + SetStencilTest::Unused2::Get(arg1) != 0) + return parse_error::kParseInvalidArguments; + unsigned int write_mask = SetStencilTest::WriteMask::Get(arg0); + unsigned int compare_mask = SetStencilTest::CompareMask::Get(arg0); + unsigned int ref = SetStencilTest::ReferenceValue::Get(arg0); + bool enable = SetStencilTest::Enable::Get(arg0) != 0; + bool separate_ccw = SetStencilTest::SeparateCCW::Get(arg0) != 0; + gapi_->SetStencilTest(enable, separate_ccw, write_mask, compare_mask, ref, + arg1); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetColorWrite( + uint32 arg_count, + const SetColorWrite& args) { + Uint32 enables = args.flags; + if (SetColorWrite::Unused::Get(enables) != 0) + return parse_error::kParseInvalidArguments; + bool red = SetColorWrite::RedMask::Get(enables) != 0; + bool green = SetColorWrite::GreenMask::Get(enables) != 0; + bool blue = SetColorWrite::BlueMask::Get(enables) != 0; + bool alpha = SetColorWrite::AlphaMask::Get(enables) != 0; + bool dither = SetColorWrite::DitherEnable::Get(enables) != 0; + gapi_->SetColorWrite(red, green, blue, alpha, dither); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetBlending( + uint32 arg_count, + const SetBlending& args) { + Uint32 arg = args.blend_settings; + bool enable = SetBlending::Enable::Get(arg) != 0; + bool separate_alpha = SetBlending::SeparateAlpha::Get(arg) != 0; + unsigned int color_eq = SetBlending::ColorEq::Get(arg); + unsigned int color_src = SetBlending::ColorSrcFunc::Get(arg); + unsigned int color_dst = SetBlending::ColorDstFunc::Get(arg); + unsigned int alpha_eq = SetBlending::AlphaEq::Get(arg); + unsigned int alpha_src = SetBlending::AlphaSrcFunc::Get(arg); + unsigned int alpha_dst = SetBlending::AlphaDstFunc::Get(arg); + if (SetBlending::Unused0::Get(arg) != 0 || + SetBlending::Unused1::Get(arg) != 0 || + color_eq >= kNumBlendEq || + color_src >= kNumBlendFunc || + color_dst >= kNumBlendFunc || + alpha_eq >= kNumBlendEq || + alpha_src >= kNumBlendFunc || + alpha_dst >= kNumBlendFunc) + return parse_error::kParseInvalidArguments; + gapi_->SetBlending(enable, + separate_alpha, + static_cast<BlendEq>(color_eq), + static_cast<BlendFunc>(color_src), + static_cast<BlendFunc>(color_dst), + static_cast<BlendEq>(alpha_eq), + static_cast<BlendFunc>(alpha_src), + static_cast<BlendFunc>(alpha_dst)); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleSetBlendingColor( + uint32 arg_count, + const SetBlendingColor& args) { + RGBA rgba; + rgba.red = args.red; + rgba.green = args.green; + rgba.blue = args.blue; + rgba.alpha = args.alpha; + gapi_->SetBlendingColor(rgba); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIDecoder::HandleCreateRenderSurface( + uint32 arg_count, + const CreateRenderSurface& args) { + unsigned int width_height = args.width_height; + unsigned int width = CreateRenderSurface::Width::Get(width_height); + unsigned int height = CreateRenderSurface::Height::Get(width_height); + unsigned int levels_side = args.levels_side; + unsigned int mip_level = CreateRenderSurface::Levels::Get(levels_side); + unsigned int side = CreateRenderSurface::Side::Get(levels_side); + return gapi_->CreateRenderSurface(args.render_surface_id, + width, height, mip_level, + side, args.texture_id); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyRenderSurface( + uint32 arg_count, + const DestroyRenderSurface& args) { + return gapi_->DestroyRenderSurface(args.render_surface_id); +} + +parse_error::ParseError GAPIDecoder::HandleCreateDepthSurface( + uint32 arg_count, + const CreateDepthSurface& args) { + unsigned int width_height = args.width_height; + unsigned int width = CreateDepthSurface::Width::Get(width_height); + unsigned int height = CreateDepthSurface::Height::Get(width_height); + return gapi_->CreateDepthSurface(args.depth_surface_id, width, height); +} + +parse_error::ParseError GAPIDecoder::HandleDestroyDepthSurface( + uint32 arg_count, + const DestroyDepthSurface& args) { + return gapi_->DestroyDepthSurface(args.depth_surface_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetRenderSurface( + uint32 arg_count, + const SetRenderSurface& args) { + return gapi_->SetRenderSurface(args.render_surface_id, args.depth_surface_id); +} + +parse_error::ParseError GAPIDecoder::HandleSetBackSurfaces( + uint32 arg_count, + const SetBackSurfaces& args) { + gapi_->SetBackSurfaces(); + return parse_error::kParseNoError; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/gapi_decoder.h b/o3d/gpu/command_buffer/service/gapi_decoder.h new file mode 100644 index 0000000..e043598 --- /dev/null +++ b/o3d/gpu/command_buffer/service/gapi_decoder.h @@ -0,0 +1,81 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the GAPI decoder class. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_GAPI_DECODER_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_GAPI_DECODER_H_ + +#include "gpu/command_buffer/service/common_decoder.h" +#include "gpu/command_buffer/common/o3d_cmd_format.h" + +namespace command_buffer { +namespace o3d { + +class GAPIInterface; + +// This class implements the AsyncAPIInterface interface, decoding GAPI +// commands and sending them to a GAPI interface. +class GAPIDecoder : public CommonDecoder { + public: + typedef parse_error::ParseError ParseError; + + explicit GAPIDecoder(GAPIInterface *gapi) : gapi_(gapi) {} + virtual ~GAPIDecoder() {} + + // Overridden from AsyncAPIInterface. + virtual ParseError DoCommand(unsigned int command, + unsigned int arg_count, + const void* args); + + // Overridden from AsyncAPIInterface. + virtual const char* GetCommandName(unsigned int command_id) const; + + private: + // Generate a member function prototype for each command in an automated and + // typesafe way. + #define GPU_COMMAND_BUFFER_CMD_OP(name) \ + ParseError Handle ## name( \ + unsigned int arg_count, \ + const o3d::name& args); \ + + O3D_COMMAND_BUFFER_CMDS(GPU_COMMAND_BUFFER_CMD_OP) + + #undef GPU_COMMAND_BUFFER_CMD_OP + + GAPIInterface *gapi_; +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_GAPI_DECODER_H_ diff --git a/o3d/gpu/command_buffer/service/gapi_gl.cc b/o3d/gpu/command_buffer/service/gapi_gl.cc new file mode 100644 index 0000000..c5f82de --- /dev/null +++ b/o3d/gpu/command_buffer/service/gapi_gl.cc @@ -0,0 +1,420 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the GAPIGL class. + +#include "gpu/command_buffer/service/precompile.h" + +#include <build/build_config.h> + +#include "gpu/command_buffer/service/gapi_gl.h" + +#ifdef OS_LINUX +#include "gpu/command_buffer/service/linux/x_utils.h" +#endif // OS_LINUX + +namespace command_buffer { +namespace o3d { + +GAPIGL::GAPIGL() + : cg_context_(NULL), +#ifdef OS_LINUX + window_(NULL), +#endif +#ifdef OS_WIN + hwnd_(NULL), + device_context_(NULL), + gl_context_(NULL), +#endif + anti_aliased_(false), + current_vertex_struct_(kInvalidResource), + validate_streams_(true), + max_vertices_(0), + current_effect_id_(kInvalidResource), + validate_effect_(true), + current_effect_(NULL) { +} + +GAPIGL::~GAPIGL() { +} + +bool GAPIGL::Initialize() { + if (!InitPlatformSpecific()) + return false; + if (!InitCommon()) + return false; + CHECK_GL_ERROR(); + return true; +} + +#if defined(OS_WIN) +static const PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = { + sizeof(kPixelFormatDescriptor), // Size of structure. + 1, // Default version. + PFD_DRAW_TO_WINDOW | // Window drawing support. + PFD_SUPPORT_OPENGL | // OpenGL support. + PFD_DOUBLEBUFFER, // Double buffering support (not stereo). + PFD_TYPE_RGBA, // RGBA color mode (not indexed). + 24, // 24 bit color mode. + 0, 0, 0, 0, 0, 0, // Don't set RGB bits & shifts. + 8, 0, // 8 bit alpha + 0, // No accumulation buffer. + 0, 0, 0, 0, // Ignore accumulation bits. + 24, // 24 bit z-buffer size. + 8, // 8-bit stencil buffer. + 0, // No aux buffer. + PFD_MAIN_PLANE, // Main drawing plane (not overlay). + 0, // Reserved. + 0, 0, 0, // Layer masks ignored. +}; + +LRESULT CALLBACK IntermediateWindowProc(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param) { + return ::DefWindowProc(window, message, w_param, l_param); +} + +// Helper routine that returns the highest quality pixel format supported on +// the current platform. Returns true upon success. +static bool GetWindowsPixelFormat(HWND window, + bool anti_aliased, + int* pixel_format) { + // We must initialize a GL context before we can determine the multi-sampling + // supported on the current hardware, so we create an intermediate window + // and context here. + HINSTANCE module_handle; + if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast<wchar_t*>(IntermediateWindowProc), + &module_handle)) { + return false; + } + + WNDCLASS intermediate_class; + intermediate_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + intermediate_class.lpfnWndProc = IntermediateWindowProc; + intermediate_class.cbClsExtra = 0; + intermediate_class.cbWndExtra = 0; + intermediate_class.hInstance = module_handle; + intermediate_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + intermediate_class.hCursor = LoadCursor(NULL, IDC_ARROW); + intermediate_class.hbrBackground = NULL; + intermediate_class.lpszMenuName = NULL; + intermediate_class.lpszClassName = L"Intermediate GL Window"; + + ATOM class_registration = ::RegisterClass(&intermediate_class); + if (!class_registration) { + return false; + } + + HWND intermediate_window = ::CreateWindow( + reinterpret_cast<wchar_t*>(class_registration), + L"", + WS_OVERLAPPEDWINDOW, + 0, 0, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, + NULL, + NULL, + NULL); + + if (!intermediate_window) { + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + + HDC intermediate_dc = ::GetDC(intermediate_window); + int format_index = ::ChoosePixelFormat(intermediate_dc, + &kPixelFormatDescriptor); + if (format_index == 0) { + DLOG(ERROR) << "Unable to get the pixel format for GL context."; + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + if (!::SetPixelFormat(intermediate_dc, format_index, + &kPixelFormatDescriptor)) { + DLOG(ERROR) << "Unable to set the pixel format for GL context."; + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + + // Store the pixel format without multisampling. + *pixel_format = format_index; + HGLRC gl_context = ::wglCreateContext(intermediate_dc); + if (::wglMakeCurrent(intermediate_dc, gl_context)) { + // GL context was successfully created and applied to the window's DC. + // Startup GLEW, the GL extensions wrangler. + GLenum glew_error = ::glewInit(); + if (glew_error == GLEW_OK) { + DLOG(INFO) << "Initialized GLEW " << ::glewGetString(GLEW_VERSION); + } else { + DLOG(ERROR) << "Unable to initialise GLEW : " + << ::glewGetErrorString(glew_error); + ::wglMakeCurrent(intermediate_dc, NULL); + ::wglDeleteContext(gl_context); + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return false; + } + + // If the multi-sample extensions are present, query the api to determine + // the pixel format. + if (anti_aliased && WGLEW_ARB_pixel_format && WGLEW_ARB_multisample) { + int pixel_attributes[] = { + WGL_SAMPLES_ARB, 4, + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_COLOR_BITS_ARB, 24, + WGL_ALPHA_BITS_ARB, 8, + WGL_DEPTH_BITS_ARB, 24, + WGL_STENCIL_BITS_ARB, 8, + WGL_DOUBLE_BUFFER_ARB, GL_TRUE, + WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, + 0, 0}; + + float pixel_attributes_f[] = {0, 0}; + int msaa_pixel_format; + unsigned int num_formats; + + // Query for the highest sampling rate supported, starting at 4x. + static const int kSampleCount[] = {4, 2}; + static const int kNumSamples = 2; + for (int sample = 0; sample < kNumSamples; ++sample) { + pixel_attributes[1] = kSampleCount[sample]; + if (GL_TRUE == ::wglChoosePixelFormatARB(intermediate_dc, + pixel_attributes, + pixel_attributes_f, + 1, + &msaa_pixel_format, + &num_formats)) { + *pixel_format = msaa_pixel_format; + break; + } + } + } + } + + ::wglMakeCurrent(intermediate_dc, NULL); + ::wglDeleteContext(gl_context); + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return true; +} +#endif + +bool GAPIGL::InitPlatformSpecific() { +#if defined(OS_WIN) + device_context_ = ::GetDC(hwnd_); + + int pixel_format; + + if (!GetWindowsPixelFormat(hwnd_, + anti_aliased(), + &pixel_format)) { + DLOG(ERROR) << "Unable to determine optimal pixel format for GL context."; + return false; + } + + if (!::SetPixelFormat(device_context_, pixel_format, + &kPixelFormatDescriptor)) { + DLOG(ERROR) << "Unable to set the pixel format for GL context."; + return false; + } + + gl_context_ = ::wglCreateContext(device_context_); + if (!gl_context_) { + DLOG(ERROR) << "Failed to create GL context."; + return false; + } + + if (!::wglMakeCurrent(device_context_, gl_context_)) { + DLOG(ERROR) << "Unable to make gl context current."; + return false; + } +#elif defined(OS_LINUX) + DCHECK(window_); + if (!window_->Initialize()) + return false; + if (!window_->MakeCurrent()) + return false; +#endif + + return true; +} + +bool GAPIGL::InitCommon() { + if (!InitGlew()) + return false; + InitCG(); + + // Initialize global GL settings. + // Tell GL that texture buffers can be single-byte aligned. + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + // Get the initial viewport (set to the window size) to set up the helper + // constant. + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + SetViewport(viewport[0], viewport[1], viewport[2], viewport[3], 0.f, 1.f); + CHECK_GL_ERROR(); + + ::glGenFramebuffersEXT(1, &render_surface_framebuffer_); + CHECK_GL_ERROR(); + return true; +} + +void GAPIGL::InitCG() { + cg_context_ = cgCreateContext(); + // Set up all Cg State Assignments for OpenGL. + cgGLRegisterStates(cg_context_); + cgGLSetDebugMode(CG_FALSE); + // Enable the profiles we use. + cgGLEnableProfile(CG_PROFILE_ARBVP1); + cgGLEnableProfile(CG_PROFILE_ARBFP1); +} + +bool GAPIGL::InitGlew() { + DLOG(INFO) << "Initializing GL and GLEW for GAPI."; + + GLenum glew_error = glewInit(); + if (glew_error != GLEW_OK) { + DLOG(ERROR) << "Unable to initialise GLEW : " + << ::glewGetErrorString(glew_error); + return false; + } + + // Check to see that we can use the OpenGL vertex attribute APIs + // TODO(petersont): Return false if this check fails, but because some + // Intel hardware does not support OpenGL 2.0, yet does support all of the + // extensions we require, we only log an error. A future CL should change + // this check to ensure that all of the extension strings we require are + // present. + if (!GLEW_VERSION_2_0) { + DLOG(ERROR) << "GL drivers do not have OpenGL 2.0 functionality."; + } + + bool extensions_found = true; + if (!GLEW_ARB_vertex_buffer_object) { + // NOTE: Linux NVidia drivers claim to support OpenGL 2.0 when using + // indirect rendering (e.g. remote X), but it is actually lying. The + // ARB_vertex_buffer_object functions silently no-op (!) when using + // indirect rendering, leading to crashes. Fortunately, in that case, the + // driver claims to not support ARB_vertex_buffer_object, so fail in that + // case. + DLOG(ERROR) << "GL drivers do not support vertex buffer objects."; + extensions_found = false; + } + if (!GLEW_EXT_framebuffer_object) { + DLOG(ERROR) << "GL drivers do not support framebuffer objects."; + extensions_found = false; + } + // Check for necessary extensions + if (!GLEW_VERSION_2_0 && !GLEW_EXT_stencil_two_side) { + DLOG(ERROR) << "Two sided stencil extension missing."; + extensions_found = false; + } + if (!GLEW_VERSION_1_4 && !GLEW_EXT_blend_func_separate) { + DLOG(ERROR) <<"Separate blend func extension missing."; + extensions_found = false; + } + if (!GLEW_VERSION_2_0 && !GLEW_EXT_blend_equation_separate) { + DLOG(ERROR) << "Separate blend function extension missing."; + extensions_found = false; + } + if (!extensions_found) + return false; + + return true; +} + +void GAPIGL::Destroy() { + vertex_buffers_.DestroyAllResources(); + index_buffers_.DestroyAllResources(); + vertex_structs_.DestroyAllResources(); + effects_.DestroyAllResources(); + effect_params_.DestroyAllResources(); + // textures_.DestroyAllResources(); + // samplers_.DestroyAllResources(); + cgDestroyContext(cg_context_); + cg_context_ = NULL; +#ifdef OS_LINUX + DCHECK(window_); + window_->Destroy(); +#endif +} + +void GAPIGL::BeginFrame() { +} + +void GAPIGL::EndFrame() { +#ifdef OS_WIN + ::SwapBuffers(device_context_); +#endif + +#ifdef OS_LINUX + DCHECK(window_); + window_->SwapBuffers(); +#endif + + CHECK_GL_ERROR(); +} + +void GAPIGL::Clear(unsigned int buffers, + const RGBA &color, + float depth, + unsigned int stencil) { + glClearColor(color.red, color.green, color.blue, color.alpha); + glClearDepth(depth); + glClearStencil(stencil); + glClear((buffers & kColor ? GL_COLOR_BUFFER_BIT : 0) | + (buffers & kDepth ? GL_DEPTH_BUFFER_BIT : 0) | + (buffers & kStencil ? GL_STENCIL_BUFFER_BIT : 0)); + CHECK_GL_ERROR(); +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/gapi_gl.h b/o3d/gpu/command_buffer/service/gapi_gl.h new file mode 100644 index 0000000..0719fba --- /dev/null +++ b/o3d/gpu/command_buffer/service/gapi_gl.h @@ -0,0 +1,465 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the GAPIGL class, implementing the GAPI interface for +// GL. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_GAPI_GL_H_ +#define GPU_COMMAND_BUFFER_SERVICE_GAPI_GL_H_ + +#include <build/build_config.h> +#include <GL/glew.h> +#include <Cg/cg.h> +#include <Cg/cgGL.h> +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/gl_utils.h" +#include "gpu/command_buffer/service/effect_gl.h" +#include "gpu/command_buffer/service/geometry_gl.h" +#include "gpu/command_buffer/service/render_surface_gl.h" +#include "gpu/command_buffer/service/sampler_gl.h" +#include "gpu/command_buffer/service/texture_gl.h" + +namespace command_buffer { +namespace o3d { +#if defined(OS_LINUX) +class XWindowWrapper; +#endif // defined(OS_LINUX) + +// This class implements the GAPI interface for GL. +class GAPIGL : public GAPIInterface { + public: + GAPIGL(); + virtual ~GAPIGL(); + +#if defined(OS_LINUX) + void set_window_wrapper(XWindowWrapper *window) { window_ = window; } +#elif defined(OS_WIN) + void set_hwnd(HWND hwnd) { hwnd_ = hwnd; } + + HWND hwnd() const { + return hwnd_; + } +#endif + + // Initializes the graphics context, bound to a window. + // Returns: + // true if successful. + virtual bool Initialize(); + + // Initailizes Cg. + void InitCG(); + + // Helper function for Initailize that inits just glew. + bool InitGlew(); + + // Destroys the graphics context. + virtual void Destroy(); + + // Implements the BeginFrame function for GL. + virtual void BeginFrame(); + + // Implements the EndFrame function for GL. + virtual void EndFrame(); + + // Implements the Clear function for GL. + virtual void Clear(unsigned int buffers, + const RGBA &color, + float depth, + unsigned int stencil); + + // Implements the SetViewport function for GL. + virtual void SetViewport(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height, + float z_min, + float z_max); + + // Implements the CreateVertexBuffer function for GL. + virtual ParseError CreateVertexBuffer(ResourceId id, + unsigned int size, + unsigned int flags); + + // Implements the DestroyVertexBuffer function for GL. + virtual ParseError DestroyVertexBuffer(ResourceId id); + + // Implements the SetVertexBufferData function for GL. + virtual ParseError SetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data); + + // Implements the GetVertexBufferData function for GL. + virtual ParseError GetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data); + + // Implements the CreateIndexBuffer function for GL. + virtual ParseError CreateIndexBuffer(ResourceId id, + unsigned int size, + unsigned int flags); + + // Implements the DestroyIndexBuffer function for GL. + virtual ParseError DestroyIndexBuffer(ResourceId id); + + // Implements the SetIndexBufferData function for GL. + virtual ParseError SetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data); + + // Implements the GetIndexBufferData function for GL. + virtual ParseError GetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data); + + // Implements the CreateVertexStruct function for GL. + virtual ParseError CreateVertexStruct(ResourceId id, + unsigned int input_count); + + // Implements the DestroyVertexStruct function for GL. + virtual ParseError DestroyVertexStruct(ResourceId id); + + // Implements the SetVertexInput function for GL. + virtual ParseError SetVertexInput(ResourceId vertex_struct_id, + unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index); + + // Implements the SetVertexStruct function for GL. + virtual ParseError SetVertexStruct(ResourceId id); + + // Implements the Draw function for GL. + virtual ParseError Draw(PrimitiveType primitive_type, + unsigned int first, + unsigned int count); + + // Implements the DrawIndexed function for GL. + virtual ParseError DrawIndexed(PrimitiveType primitive_type, + ResourceId index_buffer_id, + unsigned int first, + unsigned int count, + unsigned int min_index, + unsigned int max_index); + + // Implements the CreateEffect function for GL. + virtual ParseError CreateEffect(ResourceId id, + unsigned int size, + const void *data); + + // Implements the DestroyEffect function for GL. + virtual ParseError DestroyEffect(ResourceId id); + + // Implements the SetEffect function for GL. + virtual ParseError SetEffect(ResourceId id); + + // Implements the GetParamCount function for GL. + virtual ParseError GetParamCount(ResourceId id, + unsigned int size, + void *data); + + // Implements the CreateParam function for GL. + virtual ParseError CreateParam(ResourceId param_id, + ResourceId effect_id, + unsigned int index); + + // Implements the CreateParamByName function for GL. + virtual ParseError CreateParamByName(ResourceId param_id, + ResourceId effect_id, + unsigned int size, + const void *name); + + // Implements the DestroyParam function for GL. + virtual ParseError DestroyParam(ResourceId id); + + // Implements the SetParamData function for GL. + virtual ParseError SetParamData(ResourceId id, + unsigned int size, + const void *data); + + // Implements the GetParamDesc function for GL. + virtual ParseError GetParamDesc(ResourceId id, + unsigned int size, + void *data); + + // Implements the GetStreamCount function for GL. + virtual ParseError GetStreamCount(ResourceId id, + unsigned int size, + void *data); + + // Implements the GetStreamDesc function for GL. + virtual ParseError GetStreamDesc(ResourceId id, + unsigned int index, + unsigned int size, + void *data); + + // Implements the CreateTexture2D function for GL. + virtual ParseError CreateTexture2D(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Implements the CreateTexture3D function for GL. + virtual ParseError CreateTexture3D(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Implements the CreateTextureCube function for GL. + virtual ParseError CreateTextureCube(ResourceId id, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Implements the SetTextureData function for GL. + virtual ParseError SetTextureData(ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + + // Implements the GetTextureData function for GL. + virtual ParseError GetTextureData(ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + + // Implements the DestroyTexture function for GL. + virtual ParseError DestroyTexture(ResourceId id); + + // Implements the CreateSampler function for GL. + virtual ParseError CreateSampler(ResourceId id); + + // Implements the DestroySampler function for GL. + virtual ParseError DestroySampler(ResourceId id); + + // Implements the SetSamplerStates function for GL. + virtual ParseError SetSamplerStates(ResourceId id, + sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy); + + // Implements the SetSamplerBorderColor function for GL. + virtual ParseError SetSamplerBorderColor(ResourceId id, const RGBA &color); + + // Implements the SetSamplerTexture function for GL. + virtual ParseError SetSamplerTexture(ResourceId id, ResourceId texture_id); + + // Implements the SetScissor function for GL. + virtual void SetScissor(bool enable, + unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height); + + // Implements the SetPointLineRaster function for GL. + virtual void SetPointLineRaster(bool line_smooth, + bool point_sprite, + float point_size); + + // Implements the SetPolygonOffset function for GL. + virtual void SetPolygonOffset(float slope_factor, float units); + + // Implements the SetPolygonRaster function for GL. + virtual void SetPolygonRaster(PolygonMode fill_mode, + FaceCullMode cull_mode); + + // Implements the SetAlphaTest function for GL. + virtual void SetAlphaTest(bool enable, + float reference, + Comparison comp); + + // Implements the SetDepthTest function for GL. + virtual void SetDepthTest(bool enable, + bool write_enable, + Comparison comp); + + // Implements the SetStencilTest function for GL. + virtual void SetStencilTest(bool enable, + bool separate_ccw, + unsigned int write_mask, + unsigned int compare_mask, + unsigned int ref, + Uint32 func_ops); + + // Implements the SetColorWritefunction for GL. + virtual void SetColorWrite(bool red, + bool green, + bool blue, + bool alpha, + bool dither); + + // Implements the SetBlending function for GL. + virtual void SetBlending(bool enable, + bool separate_alpha, + BlendEq color_eq, + BlendFunc color_src_func, + BlendFunc color_dst_func, + BlendEq alpha_eq, + BlendFunc alpha_src_func, + BlendFunc alpha_dst_func); + + // Implements the SetBlendingColor function for GL. + virtual void SetBlendingColor(const RGBA &color); + + // Implements the CreateRenderSurface function for GL. + virtual ParseError CreateRenderSurface(ResourceId id, + unsigned int width, + unsigned int height, + unsigned int mip_level, + unsigned int side, + ResourceId texture_id); + + // Implements the DestroyRenderSurface function for GL. + virtual ParseError DestroyRenderSurface(ResourceId id); + + // Implements the CreateDepthSurface function for GL. + virtual ParseError CreateDepthSurface(ResourceId id, + unsigned int width, + unsigned int height); + + // Implements the DestroyDepthSurface function for GL. + virtual ParseError DestroyDepthSurface(ResourceId id); + + // Implements the SetRenderSurface function for GL. + virtual ParseError SetRenderSurface(ResourceId render_surface_id, + ResourceId depth_stencil_id); + + // Implements the SetBackSurfaces function for GL. + virtual void SetBackSurfaces(); + + // Gets a vertex buffer by resource ID. + VertexBufferGL *GetVertexBuffer(ResourceId id) { + return vertex_buffers_.Get(id); + } + + // Gets a texture by resource ID. + TextureGL *GetTexture(ResourceId id) { + return textures_.Get(id); + } + + // Gets a sampler by resource ID. + SamplerGL *GetSampler(ResourceId id) { + return samplers_.Get(id); + } + void set_anti_aliased(bool anti_aliased) { anti_aliased_ = anti_aliased; } + + bool anti_aliased() const { return anti_aliased_; } + + CGcontext cg_context() const { return cg_context_; } + + EffectGL *current_effect() const { return current_effect_; } + // "Dirty" the current effect. This resets the vertex and fragment program, + // and requires ValidateEffect() to be called before further draws occur. + void DirtyEffect(); + private: + bool InitPlatformSpecific(); + bool InitCommon(); + // Validates the current vertex struct to GL, setting the vertex attributes. + bool ValidateStreams(); + // Validates the current effect to GL. This sets the vertex and fragment + // programs, and updates parameters if needed. + bool ValidateEffect(); + + CGcontext cg_context_; + +#if defined(OS_LINUX) + XWindowWrapper *window_; +#elif defined(OS_WIN) + // Handle to the GL device. + HWND hwnd_; + HDC device_context_; + HGLRC gl_context_; +#endif + + bool anti_aliased_; + ResourceId current_vertex_struct_; + bool validate_streams_; + unsigned int max_vertices_; + ResourceId current_effect_id_; + bool validate_effect_; + EffectGL *current_effect_; + ResourceId current_surface_id_; + ResourceId current_depth_surface_id_; + GLuint render_surface_framebuffer_; + + ResourceMap<VertexBufferGL> vertex_buffers_; + ResourceMap<IndexBufferGL> index_buffers_; + ResourceMap<VertexStructGL> vertex_structs_; + ResourceMap<EffectGL> effects_; + ResourceMap<EffectParamGL> effect_params_; + ResourceMap<TextureGL> textures_; + ResourceMap<SamplerGL> samplers_; + ResourceMap<RenderSurfaceGL> render_surfaces_; + ResourceMap<RenderDepthStencilSurfaceGL> depth_surfaces_; +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_GAPI_GL_H_ diff --git a/o3d/gpu/command_buffer/service/geometry_d3d9.cc b/o3d/gpu/command_buffer/service/geometry_d3d9.cc new file mode 100644 index 0000000..c3b5eb5 --- /dev/null +++ b/o3d/gpu/command_buffer/service/geometry_d3d9.cc @@ -0,0 +1,439 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the D3D9 versions of the +// VertexBuffer, IndexBuffer and VertexStruct resources. +// This file also contains the related GAPID3D9 function implementations. + +#include "gpu/command_buffer/service/precompile.h" + +#include <algorithm> + +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/geometry_d3d9.h" +#include "gpu/command_buffer/service/gapi_d3d9.h" + +namespace command_buffer { +namespace o3d { + +// Destroys the D3D9 vertex buffer. +VertexBufferD3D9::~VertexBufferD3D9() { + DCHECK(d3d_vertex_buffer_ != NULL); + if (d3d_vertex_buffer_) { + d3d_vertex_buffer_->Release(); + d3d_vertex_buffer_ = NULL; + } +} + +// Creates a D3D9 vertex buffer. +void VertexBufferD3D9::Create(GAPID3D9 *gapi) { + DCHECK(d3d_vertex_buffer_ == NULL); + + DWORD d3d_usage = (flags() & vertex_buffer::kDynamic) ? D3DUSAGE_DYNAMIC : 0; + D3DPOOL d3d_pool = D3DPOOL_MANAGED; + HR(gapi->d3d_device()->CreateVertexBuffer(size(), d3d_usage, 0, d3d_pool, + &d3d_vertex_buffer_, NULL)); +} + +// Sets the data into the D3D9 vertex buffer, using Lock() and memcpy. +bool VertexBufferD3D9::SetData(unsigned int offset, + unsigned int size, + const void *data) { + if (!d3d_vertex_buffer_) { + LOG(ERROR) << "Calling SetData on a non-initialized VertexBufferD3D9."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on VertexBufferD3D9::SetData."; + return false; + } + void *ptr = NULL; + DWORD lock_flags = 0; + // If we are setting the full buffer, discard the old data. That's only + // possible to do for a dynamic d3d vertex buffer. + if ((offset == 0) && (size == this->size()) && + (flags() & vertex_buffer::kDynamic)) + lock_flags = D3DLOCK_DISCARD; + HR(d3d_vertex_buffer_->Lock(offset, size, &ptr, lock_flags)); + memcpy(ptr, data, size); + HR(d3d_vertex_buffer_->Unlock()); + return true; +} + +// Gets the data from the D3D9 vertex buffer, using Lock() and memcpy. +bool VertexBufferD3D9::GetData(unsigned int offset, + unsigned int size, + void *data) { + if (!d3d_vertex_buffer_) { + LOG(ERROR) << "Calling SetData on a non-initialized VertexBufferD3D9."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on VertexBufferD3D9::SetData."; + return false; + } + void *ptr = NULL; + DWORD lock_flags = D3DLOCK_READONLY; + HR(d3d_vertex_buffer_->Lock(offset, size, &ptr, lock_flags)); + memcpy(data, ptr, size); + HR(d3d_vertex_buffer_->Unlock()); + return true; +} + +// Destroys the D3D9 index buffer. +IndexBufferD3D9::~IndexBufferD3D9() { + DCHECK(d3d_index_buffer_ != NULL); + if (d3d_index_buffer_) { + d3d_index_buffer_->Release(); + d3d_index_buffer_ = NULL; + } +} + +// Creates a D3D9 index buffer. +void IndexBufferD3D9::Create(GAPID3D9 *gapi) { + DCHECK(d3d_index_buffer_ == NULL); + + DWORD d3d_usage = (flags() & index_buffer::kDynamic) ? D3DUSAGE_DYNAMIC : 0; + D3DFORMAT d3d_format = + (flags() & index_buffer::kIndex32Bit) ? D3DFMT_INDEX32 : D3DFMT_INDEX16; + D3DPOOL d3d_pool = D3DPOOL_MANAGED; + HR(gapi->d3d_device()->CreateIndexBuffer(size(), d3d_usage, d3d_format, + d3d_pool, &d3d_index_buffer_, + NULL)); +} + +// Sets the data into the D3D9 index buffer, using Lock() and memcpy. +bool IndexBufferD3D9::SetData(unsigned int offset, + unsigned int size, + const void *data) { + if (!d3d_index_buffer_) { + LOG(ERROR) << "Calling SetData on a non-initialized IndexBufferD3D9."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on IndexBufferD3D9::SetData."; + return false; + } + void *ptr = NULL; + DWORD lock_flags = 0; + // If we are setting the full buffer, discard the old data. That's only + // possible to do for a dynamic d3d index buffer. + if ((offset == 0) && (size == this->size()) && + (flags() & index_buffer::kDynamic)) + lock_flags = D3DLOCK_DISCARD; + HR(d3d_index_buffer_->Lock(offset, size, &ptr, lock_flags)); + memcpy(ptr, data, size); + HR(d3d_index_buffer_->Unlock()); + return true; +} + +// Gets the data from the D3D9 index buffer, using Lock() and memcpy. +bool IndexBufferD3D9::GetData(unsigned int offset, + unsigned int size, + void *data) { + if (!d3d_index_buffer_) { + LOG(ERROR) << "Calling SetData on a non-initialized IndexBufferD3D9."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on IndexBufferD3D9::SetData."; + return false; + } + void *ptr = NULL; + DWORD lock_flags = D3DLOCK_READONLY; + HR(d3d_index_buffer_->Lock(offset, size, &ptr, lock_flags)); + memcpy(data, ptr, size); + HR(d3d_index_buffer_->Unlock()); + return true; +} + +// Sets the input element in the VertexStruct resource. +void VertexStructD3D9::SetInput(unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index) { + Element &element = GetElement(input_index); + element.vertex_buffer = vertex_buffer_id; + element.offset = offset; + element.stride = stride; + element.type = type; + element.semantic = semantic; + element.semantic_index = semantic_index; + dirty_ = true; +} + +// Sets the vdecl and stream sources in D3D9. Compiles them if needed. +unsigned int VertexStructD3D9::SetStreams(GAPID3D9 *gapi) { + IDirect3DDevice9 *d3d_device = gapi->d3d_device(); + if (dirty_) Compile(d3d_device); + HR(d3d_device->SetVertexDeclaration(d3d_vertex_decl_)); + unsigned int max_vertices = UINT_MAX; + for (unsigned int i = 0; i < streams_.size(); ++i) { + const StreamPair &pair = streams_[i]; + VertexBufferD3D9 *vertex_buffer = gapi->GetVertexBuffer(pair.first); + if (!vertex_buffer) { + max_vertices = 0; + continue; + } + HR(d3d_device->SetStreamSource(i, vertex_buffer->d3d_vertex_buffer(), 0, + pair.second)); + + // TODO(apatrick): A zero size stride is valid. It means the first element + // in the vertex buffer will be used for every vertex. There doesn't seem + // to be enough information here to determine whether a zero stride + // vertex buffer is big enough to contain a single element. + if (pair.second != 0) { + max_vertices = std::min(max_vertices, + vertex_buffer->size() / pair.second); + } + } + return max_vertices; +} + +// Converts a vertex_struct::Type to a D3DDECLTYPE. +static D3DDECLTYPE D3DType(vertex_struct::Type type) { + switch (type) { + case vertex_struct::kFloat1: + return D3DDECLTYPE_FLOAT1; + case vertex_struct::kFloat2: + return D3DDECLTYPE_FLOAT2; + case vertex_struct::kFloat3: + return D3DDECLTYPE_FLOAT3; + case vertex_struct::kFloat4: + return D3DDECLTYPE_FLOAT4; + case vertex_struct::kUChar4N: + return D3DDECLTYPE_UBYTE4N; + case vertex_struct::kNumTypes: + break; + } + LOG(FATAL) << "Invalid type"; + return D3DDECLTYPE_FLOAT1; +} + +// Converts a vertex_struct::Semantic to a D3DDECLUSAGE. +static D3DDECLUSAGE D3DUsage(vertex_struct::Semantic semantic) { + switch (semantic) { + case vertex_struct::kPosition: + return D3DDECLUSAGE_POSITION; + case vertex_struct::kNormal: + return D3DDECLUSAGE_NORMAL; + case vertex_struct::kColor: + return D3DDECLUSAGE_COLOR; + case vertex_struct::kTexCoord: + return D3DDECLUSAGE_TEXCOORD; + case vertex_struct::kNumSemantics: + break; + } + LOG(FATAL) << "Invalid type"; + return D3DDECLUSAGE_POSITION; +} + +// Destroys the d3d vertex declaration. +VertexStructD3D9::~VertexStructD3D9() { + Destroy(); +} + +void VertexStructD3D9::Destroy() { + if (d3d_vertex_decl_) { + d3d_vertex_decl_->Release(); + d3d_vertex_decl_ = NULL; + } + streams_.clear(); +} + +// Compiles a stream map and a d3d vertex declaration from the list of inputs. +// 2 inputs that use the same vertex buffer and stride will use the same +// d3d stream. +void VertexStructD3D9::Compile(IDirect3DDevice9 *d3d_device) { + DCHECK(dirty_); + Destroy(); + streams_.reserve(count_); + scoped_array<D3DVERTEXELEMENT9> d3d_elements( + new D3DVERTEXELEMENT9[count_ + 1]); + memset(d3d_elements.get(), 0, sizeof(D3DVERTEXELEMENT9) * (count_ + 1)); + // build streams_ like a set, but the order matters. + for (unsigned int i = 0; i < count_ ; ++i) { + Element &element = GetElement(i); + D3DVERTEXELEMENT9 &d3d_element = d3d_elements[i]; + StreamPair pair(element.vertex_buffer, element.stride); + std::vector<StreamPair>::iterator it = + std::find(streams_.begin(), streams_.end(), pair); + unsigned int stream_index = 0; + if (it == streams_.end()) { + streams_.push_back(pair); + stream_index = static_cast<unsigned int>(streams_.size() - 1); + } else { + stream_index = it - streams_.begin(); + } + d3d_element.Stream = stream_index; + d3d_element.Offset = element.offset; + d3d_element.Type = D3DType(element.type); + d3d_element.Usage = D3DUsage(element.semantic); + d3d_element.UsageIndex = element.semantic_index; + } + D3DVERTEXELEMENT9 &end = d3d_elements[count_]; + end.Stream = 0xFF; + end.Type = D3DDECLTYPE_UNUSED; + HR(d3d_device->CreateVertexDeclaration(d3d_elements.get(), + &d3d_vertex_decl_)); + dirty_ = false; +} + +// Creates and assigns a VertexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::CreateVertexBuffer( + ResourceId id, + unsigned int size, + unsigned int flags) { + VertexBufferD3D9 *vertex_buffer = new VertexBufferD3D9(size, flags); + vertex_buffer->Create(this); + vertex_buffers_.Assign(id, vertex_buffer); + return parse_error::kParseNoError; +} + +// Destroys a VertexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::DestroyVertexBuffer(ResourceId id) { + return vertex_buffers_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Copies the data into the VertexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::SetVertexBufferData( + ResourceId id, + unsigned int offset, + unsigned int size, + const void *data) { + VertexBufferD3D9 *vertex_buffer = vertex_buffers_.Get(id); + if (!vertex_buffer) return parse_error::kParseInvalidArguments; + return vertex_buffer->SetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Copies the data from the VertexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::GetVertexBufferData( + ResourceId id, + unsigned int offset, + unsigned int size, + void *data) { + VertexBufferD3D9 *vertex_buffer = vertex_buffers_.Get(id); + if (!vertex_buffer) return parse_error::kParseInvalidArguments; + return vertex_buffer->GetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Creates and assigns an IndexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::CreateIndexBuffer( + ResourceId id, + unsigned int size, + unsigned int flags) { + IndexBufferD3D9 *index_buffer = new IndexBufferD3D9(size, flags); + index_buffer->Create(this); + index_buffers_.Assign(id, index_buffer); + return parse_error::kParseNoError; +} + +// Destroys an IndexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::DestroyIndexBuffer(ResourceId id) { + return index_buffers_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Copies the data into the IndexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::SetIndexBufferData( + ResourceId id, + unsigned int offset, + unsigned int size, + const void *data) { + IndexBufferD3D9 *index_buffer = index_buffers_.Get(id); + if (!index_buffer) return parse_error::kParseInvalidArguments; + return index_buffer->SetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Copies the data from the IndexBufferD3D9 resource. +parse_error::ParseError GAPID3D9::GetIndexBufferData( + ResourceId id, + unsigned int offset, + unsigned int size, + void *data) { + IndexBufferD3D9 *index_buffer = index_buffers_.Get(id); + if (!index_buffer) return parse_error::kParseInvalidArguments; + return index_buffer->GetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Creates and assigns a VertexStructD3D9 resource. +parse_error::ParseError GAPID3D9::CreateVertexStruct( + ResourceId id, unsigned int input_count) { + if (id == current_vertex_struct_) validate_streams_ = true; + VertexStructD3D9 *vertex_struct = new VertexStructD3D9(input_count); + vertex_structs_.Assign(id, vertex_struct); + return parse_error::kParseNoError; +} + +// Destroys a VertexStructD3D9 resource. +parse_error::ParseError GAPID3D9::DestroyVertexStruct(ResourceId id) { + if (id == current_vertex_struct_) validate_streams_ = true; + return vertex_structs_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Sets an input into a VertexStructD3D9 resource. +parse_error::ParseError GAPID3D9::SetVertexInput( + ResourceId vertex_struct_id, + unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index) { + if (vertex_buffer_id == current_vertex_struct_) validate_streams_ = true; + VertexStructD3D9 *vertex_struct = vertex_structs_.Get(vertex_struct_id); + if (!vertex_struct || input_index >= vertex_struct->count()) + return parse_error::kParseInvalidArguments; + vertex_struct->SetInput(input_index, vertex_buffer_id, offset, stride, type, + semantic, semantic_index); + return parse_error::kParseNoError; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/geometry_d3d9.h b/o3d/gpu/command_buffer/service/geometry_d3d9.h new file mode 100644 index 0000000..50f534d --- /dev/null +++ b/o3d/gpu/command_buffer/service/geometry_d3d9.h @@ -0,0 +1,126 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the D3D9 versions of geometry-related +// resource classes. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_GEOMETRY_D3D9_H_ +#define GPU_COMMAND_BUFFER_SERVICE_GEOMETRY_D3D9_H_ + +#include <vector> +#include <utility> +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/resource.h" + +namespace command_buffer { +namespace o3d { + +class GAPID3D9; + +// D3D9 version of VertexBuffer. +class VertexBufferD3D9 : public VertexBuffer { + public: + VertexBufferD3D9(unsigned int size, unsigned int flags) + : VertexBuffer(size, flags), + d3d_vertex_buffer_(NULL) {} + virtual ~VertexBufferD3D9(); + // Creates the D3D vertex buffer. + void Create(GAPID3D9 *gapi); + // Sets the data into the D3D vertex buffer. + bool SetData(unsigned int offset, unsigned int size, const void *data); + // Gets the data from the D3D vertex buffer. + bool GetData(unsigned int offset, unsigned int size, void *data); + + // Gets the D3D vertex buffer. + IDirect3DVertexBuffer9 * d3d_vertex_buffer() { return d3d_vertex_buffer_; } + private: + IDirect3DVertexBuffer9 *d3d_vertex_buffer_; + DISALLOW_COPY_AND_ASSIGN(VertexBufferD3D9); +}; + +// D3D9 version of IndexBuffer. +class IndexBufferD3D9 : public IndexBuffer { + public: + IndexBufferD3D9(unsigned int size, unsigned int flags) + : IndexBuffer(size, flags), + d3d_index_buffer_(NULL) {} + virtual ~IndexBufferD3D9(); + // Creates the D3D index buffer. + void Create(GAPID3D9 *gapi); + // Sets the data into the D3D index buffer. + bool SetData(unsigned int offset, unsigned int size, const void *data); + // Gets the data from the D3D index buffer. + bool GetData(unsigned int offset, unsigned int size, void *data); + + // Gets the D3D index buffer. + IDirect3DIndexBuffer9 *d3d_index_buffer() const { return d3d_index_buffer_; } + private: + IDirect3DIndexBuffer9 *d3d_index_buffer_; + DISALLOW_COPY_AND_ASSIGN(IndexBufferD3D9); +}; + +// D3D9 version of VertexStruct. +class VertexStructD3D9 : public VertexStruct { + public: + explicit VertexStructD3D9(unsigned int count) + : VertexStruct(count), + dirty_(true), + d3d_vertex_decl_(NULL) {} + virtual ~VertexStructD3D9(); + // Adds an input to the vertex struct. + void SetInput(unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index); + // Sets the input streams to D3D. + unsigned int SetStreams(GAPID3D9 *gapi); + private: + // Destroys the vertex declaration and stream map. + void Destroy(); + // Compiles the vertex declaration and stream map. + void Compile(IDirect3DDevice9 *d3d_device); + + bool dirty_; + typedef std::pair<ResourceId, unsigned int> StreamPair; + std::vector<StreamPair> streams_; + IDirect3DVertexDeclaration9 *d3d_vertex_decl_; + DISALLOW_COPY_AND_ASSIGN(VertexStructD3D9); +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_GEOMETRY_D3D9_H_ diff --git a/o3d/gpu/command_buffer/service/geometry_gl.cc b/o3d/gpu/command_buffer/service/geometry_gl.cc new file mode 100644 index 0000000..13390a9 --- /dev/null +++ b/o3d/gpu/command_buffer/service/geometry_gl.cc @@ -0,0 +1,554 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the VertexBufferGL, IndexBufferGL +// and VertexStructGL classes, as well as the geometry-related GAPI functions. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/gapi_gl.h" +#include "gpu/command_buffer/service/geometry_gl.h" + +namespace command_buffer { +namespace o3d { + +VertexBufferGL::~VertexBufferGL() { + glDeleteBuffers(1, &gl_buffer_); + CHECK_GL_ERROR(); +} + +// Creates the GL buffer object. +void VertexBufferGL::Create() { + glGenBuffers(1, &gl_buffer_); + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_); + GLenum usage = + (flags() & vertex_buffer::kDynamic) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; + glBufferData(GL_ARRAY_BUFFER, size(), NULL, usage); + CHECK_GL_ERROR(); +} + +// Sets the data into the GL buffer object. +bool VertexBufferGL::SetData(unsigned int offset, + unsigned int size, + const void *data) { + if (!gl_buffer_) { + LOG(ERROR) << "Calling SetData on a non-initialized VertexBufferGL."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on VertexBufferGL::SetData."; + return false; + } + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_); + glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); + CHECK_GL_ERROR(); + return true; +} + +// Gets the data from the GL buffer object. +bool VertexBufferGL::GetData(unsigned int offset, + unsigned int size, + void *data) { + if (!gl_buffer_) { + LOG(ERROR) << "Calling GetData on a non-initialized VertexBufferGL."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on VertexBufferGL::GetData."; + return false; + } + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_); + glGetBufferSubData(GL_ARRAY_BUFFER, offset, size, data); + CHECK_GL_ERROR(); + return true; +} + +IndexBufferGL::~IndexBufferGL() { + glDeleteBuffers(1, &gl_buffer_); +} + +// Creates the GL buffer object. +void IndexBufferGL::Create() { + glGenBuffers(1, &gl_buffer_); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_buffer_); + GLenum usage = + (flags() & vertex_buffer::kDynamic) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size(), NULL, usage); + CHECK_GL_ERROR(); +} + +// Sets the data into the GL buffer object. +bool IndexBufferGL::SetData(unsigned int offset, + unsigned int size, + const void *data) { + if (!gl_buffer_) { + LOG(ERROR) << "Calling SetData on a non-initialized IndexBufferGL."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on IndexBufferGL::SetData."; + return false; + } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_buffer_); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, size, data); + CHECK_GL_ERROR(); + return true; +} + +// Gets the data from the GL buffer object. +bool IndexBufferGL::GetData(unsigned int offset, + unsigned int size, + void *data) { + if (!gl_buffer_) { + LOG(ERROR) << "Calling GetData on a non-initialized IndexBufferGL."; + return false; + } + if ((offset >= this->size()) || (offset + size > this->size())) { + LOG(ERROR) << "Invalid size or offset on IndexBufferGL::GetData."; + return false; + } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_buffer_); + glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, size, data); + CHECK_GL_ERROR(); + return true; +} + +// Sets the input element in the VertexStruct resource. +void VertexStructGL::SetInput(unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index) { + Element &element = GetElement(input_index); + element.vertex_buffer = vertex_buffer_id; + element.offset = offset; + element.stride = stride; + element.type = type; + element.semantic = semantic; + element.semantic_index = semantic_index; + dirty_ = true; +} + +namespace { + +inline ptrdiff_t OffsetToPtrDiff(unsigned int offset) { + return static_cast<ptrdiff_t>(offset); +} + +inline void* OffsetToPtr(ptrdiff_t offset) { + return reinterpret_cast<void*>(offset); +} + +} // anonymous namespace + +unsigned int VertexStructGL::SetStreams(GAPIGL *gapi) { + if (dirty_) Compile(); + unsigned int max_vertices = UINT_MAX; + for (unsigned int i = 0; i < kMaxAttribs; ++i) { + const AttribDesc &attrib = attribs_[i]; + if (attrib.vertex_buffer_id == kInvalidResource) { + if (i == 0) { + glDisableClientState(GL_VERTEX_ARRAY); + } else { + glDisableVertexAttribArray(i); + } + } else { + glEnableVertexAttribArray(i); + VertexBufferGL *vertex_buffer = + gapi->GetVertexBuffer(attrib.vertex_buffer_id); + if (!vertex_buffer) { + glDisableVertexAttribArray(i); + max_vertices = 0; + continue; + } + DCHECK_NE(vertex_buffer->gl_buffer(), 0); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer->gl_buffer()); + glVertexAttribPointer(i, attrib.size, attrib.type, attrib.normalized, + attrib.stride, + OffsetToPtr(attrib.offset)); + max_vertices = std::min(max_vertices, + vertex_buffer->size() / attrib.stride); + } + } + CHECK_GL_ERROR(); + return max_vertices; +} + +namespace { + +// from the ARB_vertex_program extension, at +// http://www.opengl.org/registry/specs/ARB/vertex_program.txt +// +// Generic +// Attribute Conventional Attribute Conventional Attribute Command +// --------- ------------------------ ------------------------------ +// 0 vertex position Vertex +// 1 vertex weights 0-3 WeightARB, VertexWeightEXT +// 2 normal Normal +// 3 primary color Color +// 4 secondary color SecondaryColorEXT +// 5 fog coordinate FogCoordEXT +// 6 - - +// 7 - - +// 8 texture coordinate set 0 MultiTexCoord(TEXTURE0, ...) +// 9 texture coordinate set 1 MultiTexCoord(TEXTURE1, ...) +// 10 texture coordinate set 2 MultiTexCoord(TEXTURE2, ...) +// 11 texture coordinate set 3 MultiTexCoord(TEXTURE3, ...) +// 12 texture coordinate set 4 MultiTexCoord(TEXTURE4, ...) +// 13 texture coordinate set 5 MultiTexCoord(TEXTURE5, ...) +// 14 texture coordinate set 6 MultiTexCoord(TEXTURE6, ...) +// 15 texture coordinate set 7 MultiTexCoord(TEXTURE7, ...) +// 8+n texture coordinate set n MultiTexCoord(TEXTURE0+n, ...) +// +// Note: we only accept at most 8 texture coordinates for maximum compatibility +// with DirectX. + +inline unsigned int GetAttribIndex(vertex_struct::Semantic semantic, + unsigned int semantic_index) { + switch (semantic) { + case vertex_struct::kPosition: + DCHECK_EQ(semantic_index, 0); + return 0; + case vertex_struct::kNormal: + DCHECK_EQ(semantic_index, 0); + return 2; + case vertex_struct::kColor: + DCHECK_LT(semantic_index, 2U); + return 3 + semantic_index; + case vertex_struct::kTexCoord: + DCHECK_LT(semantic_index, 8U); + return 8 + semantic_index; + default: + DLOG(FATAL) << "Not reached."; + return 0; + } +} + +inline void ExtractSizeTypeNormalized(vertex_struct::Type type, + GLint *size, + GLenum *gl_type, + GLboolean *normalized) { + switch (type) { + case vertex_struct::kFloat1: + case vertex_struct::kFloat2: + case vertex_struct::kFloat3: + case vertex_struct::kFloat4: + *size = type - vertex_struct::kFloat1 + 1; + *gl_type = GL_FLOAT; + *normalized = false; + break; + case vertex_struct::kUChar4N: + *size = 4; + *gl_type = GL_UNSIGNED_BYTE; + *normalized = true; + break; + default: + DLOG(FATAL) << "Not reached."; + break; + } +} + +} // anonymous namespace + +#ifndef COMPILER_MSVC +// Although required by the spec, this causes problems on MSVC. It is needed by +// GCC. +const unsigned int VertexStructGL::kMaxAttribs; +#endif + +void VertexStructGL::Compile() { + DCHECK(dirty_); + for (unsigned int i = 0; i < kMaxAttribs; ++i) { + attribs_[i].vertex_buffer_id = kInvalidResource; + } + for (unsigned int i = 0; i < count_ ; ++i) { + const Element &element = GetElement(i); + unsigned int index = GetAttribIndex(element.semantic, + element.semantic_index); + DCHECK_LT(index, kMaxAttribs); + AttribDesc &attrib = attribs_[index]; + attrib.vertex_buffer_id = element.vertex_buffer; + ExtractSizeTypeNormalized(element.type, &attrib.size, &attrib.type, + &attrib.normalized); + attrib.stride = element.stride; + attrib.offset = OffsetToPtrDiff(element.offset); + } + dirty_ = false; +} + +parse_error::ParseError GAPIGL::CreateVertexBuffer(ResourceId id, + unsigned int size, + unsigned int flags) { + VertexBufferGL *vertex_buffer = new VertexBufferGL(size, flags); + vertex_buffer->Create(); + vertex_buffers_.Assign(id, vertex_buffer); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyVertexBuffer(ResourceId id) { + return vertex_buffers_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data) { + VertexBufferGL *vertex_buffer = vertex_buffers_.Get(id); + if (!vertex_buffer) return parse_error::kParseInvalidArguments; + return vertex_buffer->SetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::GetVertexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data) { + VertexBufferGL *vertex_buffer = vertex_buffers_.Get(id); + if (!vertex_buffer) return parse_error::kParseInvalidArguments; + return vertex_buffer->GetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::CreateIndexBuffer(ResourceId id, + unsigned int size, + unsigned int flags) { + IndexBufferGL *index_buffer = new IndexBufferGL(size, flags); + index_buffer->Create(); + index_buffers_.Assign(id, index_buffer); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyIndexBuffer(ResourceId id) { + return index_buffers_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + const void *data) { + IndexBufferGL *index_buffer = index_buffers_.Get(id); + if (!index_buffer) return parse_error::kParseInvalidArguments; + return index_buffer->SetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::GetIndexBufferData(ResourceId id, + unsigned int offset, + unsigned int size, + void *data) { + IndexBufferGL *index_buffer = index_buffers_.Get(id); + if (!index_buffer) return parse_error::kParseInvalidArguments; + return index_buffer->GetData(offset, size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::CreateVertexStruct( + ResourceId id, + unsigned int input_count) { + if (id == current_vertex_struct_) validate_streams_ = true; + VertexStructGL *vertex_struct = new VertexStructGL(input_count); + vertex_structs_.Assign(id, vertex_struct); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyVertexStruct(ResourceId id) { + if (id == current_vertex_struct_) validate_streams_ = true; + return vertex_structs_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetVertexInput( + ResourceId vertex_struct_id, + unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index) { + switch (semantic) { + case vertex_struct::kPosition: + if (semantic_index != 0) { + return parse_error::kParseInvalidArguments; + } + break; + case vertex_struct::kNormal: + if (semantic_index != 0) { + return parse_error::kParseInvalidArguments; + } + break; + case vertex_struct::kColor: + if (semantic_index >= 2) { + return parse_error::kParseInvalidArguments; + } + break; + case vertex_struct::kTexCoord: + if (semantic_index >= 8) { + return parse_error::kParseInvalidArguments; + } + break; + default: + DLOG(FATAL) << "Not reached."; + break; + } + if (vertex_buffer_id == current_vertex_struct_) validate_streams_ = true; + VertexStructGL *vertex_struct = vertex_structs_.Get(vertex_struct_id); + if (!vertex_struct || input_index >= vertex_struct->count()) + return parse_error::kParseInvalidArguments; + vertex_struct->SetInput(input_index, vertex_buffer_id, offset, stride, type, + semantic, semantic_index); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::SetVertexStruct(ResourceId id) { + current_vertex_struct_ = id; + validate_streams_ = true; + return parse_error::kParseNoError; +} + +bool GAPIGL::ValidateStreams() { + DCHECK(validate_streams_); + VertexStructGL *vertex_struct = vertex_structs_.Get(current_vertex_struct_); + if (!vertex_struct) { + LOG(ERROR) << "Drawing with invalid streams."; + return false; + } + max_vertices_ = vertex_struct->SetStreams(this); + validate_streams_ = false; + return max_vertices_ > 0; +} + +namespace { + +void PrimitiveTypeToGL(o3d::PrimitiveType primitive_type, + GLenum *gl_mode, + unsigned int *count) { + switch (primitive_type) { + case o3d::kPoints: + *gl_mode = GL_POINTS; + break; + case o3d::kLines: + *gl_mode = GL_LINES; + *count *= 2; + break; + case o3d::kLineStrips: + *gl_mode = GL_LINE_STRIP; + ++*count; + break; + case o3d::kTriangles: + *gl_mode = GL_TRIANGLES; + *count *= 3; + break; + case o3d::kTriangleStrips: + *gl_mode = GL_TRIANGLE_STRIP; + *count += 2; + break; + case o3d::kTriangleFans: + *gl_mode = GL_TRIANGLE_FAN; + *count += 2; + break; + default: + LOG(FATAL) << "Invalid primitive type"; + break; + } +} + +} // anonymous namespace + +parse_error::ParseError GAPIGL::Draw(o3d::PrimitiveType primitive_type, + unsigned int first, + unsigned int count) { + if (validate_effect_ && !ValidateEffect()) { + return parse_error::kParseInvalidArguments; + } + DCHECK(current_effect_); + if (validate_streams_ && !ValidateStreams()) { + return parse_error::kParseInvalidArguments; + } + GLenum gl_mode = GL_POINTS; + PrimitiveTypeToGL(primitive_type, &gl_mode, &count); + if (first + count > max_vertices_) { + return parse_error::kParseInvalidArguments; + } + glDrawArrays(gl_mode, first, count); + CHECK_GL_ERROR(); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DrawIndexed( + o3d::PrimitiveType primitive_type, + ResourceId index_buffer_id, + unsigned int first, + unsigned int count, + unsigned int min_index, + unsigned int max_index) { + IndexBufferGL *index_buffer = index_buffers_.Get(index_buffer_id); + if (!index_buffer) return parse_error::kParseInvalidArguments; + if (validate_effect_ && !ValidateEffect()) { + return parse_error::kParseInvalidArguments; + } + DCHECK(current_effect_); + if (validate_streams_ && !ValidateStreams()) { + return parse_error::kParseInvalidArguments; + } + if ((min_index >= max_vertices_) || (max_index > max_vertices_)) { + return parse_error::kParseInvalidArguments; + } + GLenum gl_mode = GL_POINTS; + PrimitiveTypeToGL(primitive_type, &gl_mode, &count); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer->gl_buffer()); + GLenum index_type = (index_buffer->flags() & index_buffer::kIndex32Bit) ? + GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + GLuint index_size = (index_buffer->flags() & index_buffer::kIndex32Bit) ? + sizeof(GLuint) : sizeof(GLushort); // NOLINT + GLuint offset = first * index_size; + if (offset + count * index_size > index_buffer->size()) { + return parse_error::kParseInvalidArguments; + } + glDrawRangeElements(gl_mode, min_index, max_index, count, index_type, + OffsetToPtr(offset)); + CHECK_GL_ERROR(); + return parse_error::kParseNoError; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/geometry_gl.h b/o3d/gpu/command_buffer/service/geometry_gl.h new file mode 100644 index 0000000..b4d4b5f --- /dev/null +++ b/o3d/gpu/command_buffer/service/geometry_gl.h @@ -0,0 +1,144 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file declares the VertexBufferGL, IndexBufferGL and VertexStructGL +// classes. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_GEOMETRY_GL_H_ +#define GPU_COMMAND_BUFFER_SERVICE_GEOMETRY_GL_H_ + +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/resource.h" +#include "gpu/command_buffer/service/gl_utils.h" + +namespace command_buffer { +namespace o3d { + +class GAPIGL; + +// GL version of VertexBuffer. +class VertexBufferGL : public VertexBuffer { + public: + VertexBufferGL(unsigned int size, unsigned int flags) + : VertexBuffer(size, flags), + gl_buffer_(0) {} + virtual ~VertexBufferGL(); + + // Creates the GL vertex buffer. + void Create(); + + // Sets the data into the GL vertex buffer. + bool SetData(unsigned int offset, unsigned int size, const void *data); + + // Gets the data from the GL vertex buffer. + bool GetData(unsigned int offset, unsigned int size, void *data); + + // Gets the GL vertex buffer. + GLuint gl_buffer() const { return gl_buffer_; } + + private: + GLuint gl_buffer_; + DISALLOW_COPY_AND_ASSIGN(VertexBufferGL); +}; + +// GL version of IndexBuffer. +class IndexBufferGL : public IndexBuffer { + public: + IndexBufferGL(unsigned int size, unsigned int flags) + : IndexBuffer(size, flags), + gl_buffer_(0) {} + virtual ~IndexBufferGL(); + + // Creates the GL index buffer. + void Create(); + + // Sets the data into the GL index buffer. + bool SetData(unsigned int offset, unsigned int size, const void *data); + + // Gets the data from the GL index buffer. + bool GetData(unsigned int offset, unsigned int size, void *data); + + // Gets the GL index buffer. + GLuint gl_buffer() const { return gl_buffer_; } + + private: + GLuint gl_buffer_; + DISALLOW_COPY_AND_ASSIGN(IndexBufferGL); +}; + +// GL version of VertexStruct. +class VertexStructGL : public VertexStruct { + public: + explicit VertexStructGL(unsigned int count) + : VertexStruct(count), + dirty_(true) {} + virtual ~VertexStructGL() {} + + // Adds an input to the vertex struct. + void SetInput(unsigned int input_index, + ResourceId vertex_buffer_id, + unsigned int offset, + unsigned int stride, + vertex_struct::Type type, + vertex_struct::Semantic semantic, + unsigned int semantic_index); + + // Sets the input streams to GL. + unsigned int SetStreams(GAPIGL *gapi); + + private: + static const unsigned int kMaxAttribs = 16; + + // This struct describes the parameters that are passed to + // glVertexAttribPointer. + struct AttribDesc { + ResourceId vertex_buffer_id; + GLint size; + GLenum type; + GLboolean normalized; + GLsizei stride; + ptrdiff_t offset; + }; + + // Compiles the vertex declaration into the attribute array. + void Compile(); + + bool dirty_; + AttribDesc attribs_[kMaxAttribs]; + DISALLOW_COPY_AND_ASSIGN(VertexStructGL); +}; + + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_GEOMETRY_GL_H_ diff --git a/o3d/gpu/command_buffer/service/gl_utils.h b/o3d/gpu/command_buffer/service/gl_utils.h new file mode 100644 index 0000000..292b2d6 --- /dev/null +++ b/o3d/gpu/command_buffer/service/gl_utils.h @@ -0,0 +1,60 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file includes all the necessary GL/Cg headers and implements some useful +// utilities. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_GL_UTILS_H_ +#define GPU_COMMAND_BUFFER_SERVICE_GL_UTILS_H_ + +#include <GL/glew.h> +#if defined(OS_WIN) +#include <GL/wglew.h> +#endif +#include <Cg/cg.h> +#include <Cg/cgGL.h> +#include <build/build_config.h> + +#define GL_GLEXT_PROTOTYPES + +// Define this for extra GL error debugging (slower). +// #define GL_ERROR_DEBUGGING +#ifdef GL_ERROR_DEBUGGING +#define CHECK_GL_ERROR() do { \ + GLenum gl_error = glGetError(); \ + LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error :" << gl_error; \ + } while (0) +#else // GL_ERROR_DEBUGGING +#define CHECK_GL_ERROR() void(0) +#endif // GL_ERROR_DEBUGGING + +#endif // GPU_COMMAND_BUFFER_SERVICE_GL_UTILS_H_ diff --git a/o3d/gpu/command_buffer/service/mocks.h b/o3d/gpu/command_buffer/service/mocks.h new file mode 100644 index 0000000..dbfb160 --- /dev/null +++ b/o3d/gpu/command_buffer/service/mocks.h @@ -0,0 +1,108 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains definitions for mock objects, used for testing. + +// TODO: This file "manually" defines some mock objects. Using gMock +// would be definitely preferable, unfortunately it doesn't work on Windows +// yet. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_MOCKS_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_MOCKS_H_ + +#include <vector> +#include "gmock/gmock.h" +#include "gpu/command_buffer/service/cmd_parser.h" +#include "gpu/command_buffer/service/cmd_buffer_engine.h" + +namespace command_buffer { + +// Mocks an AsyncAPIInterface, using GMock. +class AsyncAPIMock : public AsyncAPIInterface { + public: + AsyncAPIMock() { + testing::DefaultValue<parse_error::ParseError>::Set( + parse_error::kParseNoError); + } + + // Predicate that matches args passed to DoCommand, by looking at the values. + class IsArgs { + public: + IsArgs(unsigned int arg_count, const void* args) + : arg_count_(arg_count), + args_(static_cast<CommandBufferEntry*>(const_cast<void*>(args))) { + } + + bool operator() (const void* _args) const { + const CommandBufferEntry* args = + static_cast<const CommandBufferEntry*>(_args) + 1; + for (unsigned int i = 0; i < arg_count_; ++i) { + if (args[i].value_uint32 != args_[i].value_uint32) return false; + } + return true; + } + + private: + unsigned int arg_count_; + CommandBufferEntry *args_; + }; + + MOCK_METHOD3(DoCommand, parse_error::ParseError( + unsigned int command, + unsigned int arg_count, + const void* cmd_data)); + + const char* GetCommandName(unsigned int command_id) const { + return ""; + }; + + // Sets the engine, to forward SetToken commands to it. + void set_engine(CommandBufferEngine *engine) { engine_ = engine; } + + // Forwards the SetToken commands to the engine. + void SetToken(unsigned int command, + unsigned int arg_count, + const void* _args) { + DCHECK(engine_); + DCHECK_EQ(1u, command); + DCHECK_EQ(1u, arg_count); + const CommandBufferEntry* args = + static_cast<const CommandBufferEntry*>(_args); + engine_->set_token(args[0].value_uint32); + } + private: + CommandBufferEngine *engine_; +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_MOCKS_H_ diff --git a/o3d/gpu/command_buffer/service/precompile.cc b/o3d/gpu/command_buffer/service/precompile.cc new file mode 100644 index 0000000..3e219b0 --- /dev/null +++ b/o3d/gpu/command_buffer/service/precompile.cc @@ -0,0 +1,33 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "gpu/command_buffer/service/precompile.h" diff --git a/o3d/gpu/command_buffer/service/precompile.h b/o3d/gpu/command_buffer/service/precompile.h new file mode 100644 index 0000000..55d2b21 --- /dev/null +++ b/o3d/gpu/command_buffer/service/precompile.h @@ -0,0 +1,50 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains includes for common headers used by command buffer server +// files. It is used for pre-compiled header support. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_PRECOMPILE_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_PRECOMPILE_H_ + +#include <build/build_config.h> + +#if defined(OS_WIN) +#include <windows.h> +#endif + +#include <assert.h> +#include <algorithm> +#include <map> +#include <vector> + +#endif // O3D_CORE_CROSS_PRECOMPILE_H_ diff --git a/o3d/gpu/command_buffer/service/render_surface_d3d9.cc b/o3d/gpu/command_buffer/service/render_surface_d3d9.cc new file mode 100644 index 0000000..a3880f8 --- /dev/null +++ b/o3d/gpu/command_buffer/service/render_surface_d3d9.cc @@ -0,0 +1,230 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the D3D9 versions of the render surface resources, +// as well as the related GAPID3D9 function implementations. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/render_surface_d3d9.h" +#include "gpu/command_buffer/service/gapi_d3d9.h" +#include "gpu/command_buffer/service/texture_d3d9.h" + + +namespace command_buffer { +namespace o3d { + +RenderSurfaceD3D9::RenderSurfaceD3D9(int width, + int height, + int mip_level, + int side, + TextureD3D9 *texture, + IDirect3DSurface9 *direct3d_surface) + : width_(width), + height_(height), + mip_level_(mip_level), + texture_(texture), + direct3d_surface_(direct3d_surface) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + DCHECK_GT(mip_level, -1); + DCHECK(texture); +} + +RenderSurfaceD3D9* RenderSurfaceD3D9::Create(GAPID3D9 *gapi, + int width, + int height, + int mip_level, + int side, + TextureD3D9 *texture) { + DCHECK(gapi); + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + DCHECK_GT(mip_level, -1); + DCHECK(texture); + CComPtr<IDirect3DSurface9> direct3d_surface_handle; + bool success = + texture->CreateRenderSurface(width, height, mip_level, side, + &direct3d_surface_handle); + if (!success || direct3d_surface_handle == NULL) { + // If the surface was not created properly, delete and return nothing. + return NULL; + } + RenderSurfaceD3D9 *render_surface = + new RenderSurfaceD3D9(width, + height, + mip_level, + side, + texture, + direct3d_surface_handle); + return render_surface; +} + +RenderDepthStencilSurfaceD3D9::RenderDepthStencilSurfaceD3D9( + int width, + int height, + IDirect3DSurface9 *direct3d_surface) + : width_(width), + height_(height), + direct3d_surface_(direct3d_surface) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); +} + +RenderDepthStencilSurfaceD3D9* RenderDepthStencilSurfaceD3D9::Create( + GAPID3D9 *gapi, + int width, + int height) { + DCHECK(gapi); + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + + CComPtr<IDirect3DSurface9> direct3d_surface; + gapi->d3d_device()->CreateDepthStencilSurface( + width, + height, + D3DFMT_D24S8, // d3d format + D3DMULTISAMPLE_NONE, // multisampling type + 0, // multisample quality level + FALSE, // z-buffer discarding disabled + &direct3d_surface, + NULL); // This parameter is required to be NULL. + if (direct3d_surface == NULL) { + return NULL; + } + RenderDepthStencilSurfaceD3D9 *depth_stencil = + new RenderDepthStencilSurfaceD3D9(width, height, direct3d_surface); + return depth_stencil; +} + +// GAPI Interface functions --------------------------------------------------- + +// Copies the data from a texture resource. +parse_error::ParseError GAPID3D9::CreateRenderSurface( + ResourceId id, + unsigned int width, + unsigned int height, + unsigned int mip_level, + unsigned int side, + ResourceId texture_id) { + if (id == current_surface_id_) { + // This will delete the current surface which would be bad. + return parse_error::kParseInvalidArguments; + } + TextureD3D9 *texture = textures_.Get(texture_id); + if (!texture->render_surfaces_enabled()) { + return parse_error::kParseInvalidArguments; + } else { + RenderSurfaceD3D9 *render_surface = RenderSurfaceD3D9::Create(this, + width, + height, + mip_level, + side, + texture); + if (render_surface == NULL) { + return parse_error::kParseInvalidArguments; + } + render_surfaces_.Assign(id, render_surface); + } + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::DestroyRenderSurface(ResourceId id) { + if (id == current_surface_id_) { + return parse_error::kParseInvalidArguments; + } + return render_surfaces_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPID3D9::CreateDepthSurface( + ResourceId id, + unsigned int width, + unsigned int height) { + if (id == current_depth_surface_id_) { + // This will delete the current surface which would be bad. + return parse_error::kParseInvalidArguments; + } + RenderDepthStencilSurfaceD3D9 *depth_surface = + RenderDepthStencilSurfaceD3D9::Create(this, width, height); + if (depth_surface == NULL) { + return parse_error::kParseInvalidArguments; + } + depth_surfaces_.Assign(id, depth_surface); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::DestroyDepthSurface(ResourceId id) { + if (id == current_depth_surface_id_) { + return parse_error::kParseInvalidArguments; + } + return depth_surfaces_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPID3D9::SetRenderSurface( + ResourceId render_surface_id, + ResourceId depth_stencil_id) { + RenderSurfaceD3D9 *d3d_render_surface = + render_surfaces_.Get(render_surface_id); + RenderDepthStencilSurfaceD3D9 *d3d_render_depth_surface = + depth_surfaces_.Get(depth_stencil_id); + + if (d3d_render_surface == NULL && d3d_render_depth_surface == NULL) { + return parse_error::kParseInvalidArguments; + } + + IDirect3DSurface9 *d3d_surface = + d3d_render_surface ? d3d_render_surface->direct3d_surface() : NULL; + IDirect3DSurface9 *d3d_depth_surface = d3d_render_depth_surface ? + d3d_render_depth_surface->direct3d_surface() : NULL; + + // Get the device and set the render target and the depth stencil surface. + IDirect3DDevice9 *device = this->d3d_device(); + + HR(device->SetRenderTarget(0, d3d_surface)); + HR(device->SetDepthStencilSurface(d3d_depth_surface)); + current_surface_id_ = render_surface_id; + current_depth_surface_id_ = depth_stencil_id; + return parse_error::kParseNoError; +} + +void GAPID3D9::SetBackSurfaces() { + // Get the device and set the render target and the depth stencil surface. + IDirect3DDevice9 *device = this->d3d_device(); + HR(d3d_device()->SetRenderTarget(0, back_buffer_surface_)); + HR(d3d_device()->SetDepthStencilSurface(back_buffer_depth_surface_)); +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/render_surface_d3d9.h b/o3d/gpu/command_buffer/service/render_surface_d3d9.h new file mode 100644 index 0000000..4769f1f --- /dev/null +++ b/o3d/gpu/command_buffer/service/render_surface_d3d9.h @@ -0,0 +1,161 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef GPU_COMMAND_BUFFER_SERVICE_RENDER_SURFACE_D3D9_H_ +#define GPU_COMMAND_BUFFER_SERVICE_RENDER_SURFACE_D3D9_H_ + +// This file contains the definition of the D3D9 versions of +// render surface-related resource classes. + +#include <d3d9.h> +#include "base/scoped_ptr.h" +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/resource.h" +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/texture_d3d9.h" + +namespace command_buffer { +namespace o3d { + +class GAPID3D9; + +// The RenderSurfaceD3D class represents a render surface resource in the d3d +// backend of the command buffer server. +class RenderSurfaceD3D9 : public RenderSurface { + public: + + // Creates a render surface resource based on D3D. + // Parameters: + // width - width of the surface to be created. Must match width of texture + // at mip_level. + // height - height of the surface to be created. Must match width of + // texture at mip_level. + // mip_level - mip level of the texture to which the render surface maps. + // side - side of a cube if texture is a cube texture. Does not apply to + // texture 2d's. + // texture - the texture to which this render surface maps. Not owned by + // the RenderSurfaceD3D9. + // direct3d_surface - a surface to be used as this render surface's + // rendering surface. The new RenderSurfaceD3D9 will own the + // direct3d_surface. + RenderSurfaceD3D9(int width, + int height, + int mip_level, + int side, + TextureD3D9 *texture, + IDirect3DSurface9 *direct3d_surface); + + // Destructor for the render surface. + virtual ~RenderSurfaceD3D9() {} + + // Performs the setup necessary to create a render surface resource based on + // D3D and returns a new one if possibe. + // Parameters: + // gapi - the gapi interface to D3D. + // width - width of the surface to be created. Must match width of texture + // at mip_level. + // height - height of the surface to be created. Must match width of + // texture at mip_level. + // mip_level - mip level of the texture to which the render surface maps. + // side - side of a cube if texture is a cube texture. Does not apply to + // texture 2d's. + // texture - the texture to which this render surface maps. + // Returns: + // a new RenderSurfaceD3D9 or NULL on failure + static RenderSurfaceD3D9* Create(GAPID3D9 *gapi, + int width, + int height, + int mip_level, + int side, + TextureD3D9 *texture); + + // Returns a reference to the actual direct3d surface that is rendered to. + IDirect3DSurface9* direct3d_surface() const { + return direct3d_surface_; + } + private: + CComPtr<IDirect3DSurface9> direct3d_surface_; + int width_; + int height_; + int mip_level_; + TextureD3D9 *texture_; + DISALLOW_COPY_AND_ASSIGN(RenderSurfaceD3D9); +}; + +// The RenderDepthStencilSurfaceD3D class represents a depth stencil surface +// resource in the d3d backend of the command buffer server. +class RenderDepthStencilSurfaceD3D9 : public RenderDepthStencilSurface { + public: + + // Creates a depth stencil surface resource based on D3D. + // Parameters: + // width - width of the surface to be created. + // height - height of the surface to be created. + // direct3d_surface - a surface to be used as this depth stencil surface's + // rendering rendering surface. The new RenderDepthStencilSurfaceD3D9 + // will own the direct3d_surface. + RenderDepthStencilSurfaceD3D9(int width, + int height, + IDirect3DSurface9 *direct3d_surface); + + // Destructor for the depth stencil surface. + virtual ~RenderDepthStencilSurfaceD3D9() {} + + + // Performs the setup necessary to create a depth stencil surface resource + // based on D3D and returns a new one if possibe. + // Parameters: + // gapi - the gapi interface to D3D. + // width - width of the surface to be created. + // height - height of the surface to be created. + // Returns: + // a new RenderDepthStencilSurfaceD3D9 or NULL on failure. + static RenderDepthStencilSurfaceD3D9* Create(GAPID3D9 *gapi, + int width, + int height); + + // Returns a reference to the actual direct3d surface that is rendered to. + IDirect3DSurface9* direct3d_surface() const { + return direct3d_surface_; + } + private: + CComPtr<IDirect3DSurface9> direct3d_surface_; + int width_; + int height_; + DISALLOW_COPY_AND_ASSIGN(RenderDepthStencilSurfaceD3D9); +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_RENDER_SURFACE_D3D9_H_ + diff --git a/o3d/gpu/command_buffer/service/render_surface_gl.cc b/o3d/gpu/command_buffer/service/render_surface_gl.cc new file mode 100644 index 0000000..5485323 --- /dev/null +++ b/o3d/gpu/command_buffer/service/render_surface_gl.cc @@ -0,0 +1,262 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the OpenGL versions of the render surface resources, +// as well as the related GAPIGL function implementations. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/gapi_gl.h" +#include "gpu/command_buffer/service/render_surface_gl.h" + + +namespace command_buffer { +namespace o3d { + +RenderSurfaceGL::RenderSurfaceGL(int width, + int height, + int mip_level, + int side, + TextureGL *texture) + : width_(width), height_(height), mip_level_(mip_level), texture_(texture) { +} + +RenderSurfaceGL* RenderSurfaceGL::Create(int width, + int height, + int mip_level, + int side, + TextureGL *texture) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + DCHECK_GE(mip_level, 0); + DCHECK(texture); + + RenderSurfaceGL* render_surface = + new RenderSurfaceGL(width, height, mip_level, side, texture); + return render_surface; +} + +RenderDepthStencilSurfaceGL::RenderDepthStencilSurfaceGL( + int width, + int height) + : width_(width), height_(height) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + + // If packed depth stencil is supported, create only one buffer for both + // depth and stencil. + if (GLEW_EXT_packed_depth_stencil) { + glGenRenderbuffersEXT(1, render_buffers_); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffers_[0]); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_DEPTH24_STENCIL8_EXT, + width, + height); + CHECK_GL_ERROR(); + render_buffers_[1] = render_buffers_[0]; + } else { + glGenRenderbuffersEXT(2, render_buffers_); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffers_[0]); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_DEPTH_COMPONENT24, + width, + height); + CHECK_GL_ERROR(); + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffers_[1]); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_STENCIL_INDEX8_EXT, + width, + height); + CHECK_GL_ERROR(); + } +} + +RenderDepthStencilSurfaceGL* RenderDepthStencilSurfaceGL::Create( + int width, + int height) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + + return new RenderDepthStencilSurfaceGL(height, width); +} + +// Copies the data from a texture resource. +parse_error::ParseError GAPIGL::CreateRenderSurface( + ResourceId id, + unsigned int width, + unsigned int height, + unsigned int mip_level, + unsigned int side, + ResourceId texture_id) { + if (id == current_surface_id_) { + // This will delete the current surface which would be bad. + return parse_error::kParseInvalidArguments; + } + TextureGL *texture = textures_.Get(texture_id); + if (!texture->render_surfaces_enabled()) { + return parse_error::kParseInvalidArguments; + } else { + RenderSurfaceGL* render_surface = RenderSurfaceGL::Create(width, + height, + mip_level, + side, + texture); + if (render_surface == NULL) { + return parse_error::kParseInvalidArguments; + } + render_surfaces_.Assign(id, render_surface); + } + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyRenderSurface(ResourceId id) { + if (id == current_surface_id_) { + return parse_error::kParseInvalidArguments; + } + return render_surfaces_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::CreateDepthSurface( + ResourceId id, + unsigned int width, + unsigned int height) { + if (id == current_depth_surface_id_) { + // This will delete the current surface which would be bad. + return parse_error::kParseInvalidArguments; + } + RenderDepthStencilSurfaceGL* depth_surface = + RenderDepthStencilSurfaceGL::Create(width, height); + if (depth_surface == NULL) { + return parse_error::kParseInvalidArguments; + } + depth_surfaces_.Assign(id, depth_surface); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::DestroyDepthSurface(ResourceId id) { + if (id == current_depth_surface_id_) { + return parse_error::kParseInvalidArguments; + } + return depth_surfaces_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +void ResetBoundAttachments() { +#ifdef _DEBUG + GLint bound_framebuffer; + ::glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &bound_framebuffer); + DCHECK(bound_framebuffer != 0); +#endif + + // Reset the bound attachments to the current framebuffer object. + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_RENDERBUFFER_EXT, + 0); + + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + 0); + + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + 0); + + CHECK_GL_ERROR(); +} + +bool BindDepthStencilBuffer(const RenderDepthStencilSurfaceGL* gl_surface) { + // Bind both the depth and stencil attachments. + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + gl_surface->depth_buffer()); + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + gl_surface->stencil_buffer()); + + // Check for errors. + GLenum framebuffer_status = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (GL_FRAMEBUFFER_COMPLETE_EXT != framebuffer_status) { + return false; + } + + CHECK_GL_ERROR(); + return true; +} + +parse_error::ParseError GAPIGL::SetRenderSurface( + ResourceId render_surface_id, + ResourceId depth_stencil_id) { + if (render_surfaces_.Get(render_surface_id) == NULL && + depth_surfaces_.Get(depth_stencil_id) == NULL) { + return parse_error::kParseInvalidArguments; + } + + ::glBindFramebufferEXT(GL_FRAMEBUFFER, render_surface_framebuffer_); + ResetBoundAttachments(); + + RenderSurfaceGL* render_surface = render_surfaces_.Get(render_surface_id); + RenderDepthStencilSurfaceGL* depth_surface = + depth_surfaces_.Get(render_surface_id); + + if (!render_surface->texture()-> + InstallFrameBufferObjects(render_surface) || + !BindDepthStencilBuffer(depth_surface)) { + return parse_error::kParseInvalidArguments; + } + + // RenderSurface rendering is performed with an inverted Y, so the front + // face winding must be changed to clock-wise. See comments for + // UpdateHelperConstant. + glFrontFace(GL_CW); + + current_surface_id_ = render_surface_id; + current_depth_surface_id_ = depth_stencil_id; + return parse_error::kParseNoError; +} + +void GAPIGL::SetBackSurfaces() { + // Bind the default context, and restore the default front-face winding. + ::glBindFramebufferEXT(GL_FRAMEBUFFER, 0); + glFrontFace(GL_CCW); +} + +} // namespace o3d +} // namespace command_buffer + diff --git a/o3d/gpu/command_buffer/service/render_surface_gl.h b/o3d/gpu/command_buffer/service/render_surface_gl.h new file mode 100644 index 0000000..01d6137 --- /dev/null +++ b/o3d/gpu/command_buffer/service/render_surface_gl.h @@ -0,0 +1,117 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef GPU_COMMAND_BUFFER_SERVICE_RENDER_SURFACE_GL_H__ +#define GPU_COMMAND_BUFFER_SERVICE_RENDER_SURFACE_GL_H__ + +// This file contains the definition of the OpenGL versions of +// render surface-related resource classes. + +#include "gpu/command_buffer/service/texture_gl.h" +#include "gpu/command_buffer/service/resource.h" + +namespace command_buffer { +namespace o3d { + +class RenderSurfaceGL : public RenderSurface { + public: + RenderSurfaceGL(int width, + int height, + int mip_level, + int side, + TextureGL *texture); + virtual ~RenderSurfaceGL() {} + + static RenderSurfaceGL* Create(int width, + int height, + int mip_level, + int side, + TextureGL *texture); + TextureGL* texture() { + return texture_; + } + + int width() { + return width_; + } + + int height() { + return height_; + } + + int mip_level() { + return mip_level_; + } + + int side() { + return side_; + } + private: + unsigned int width_; + unsigned int height_; + unsigned int mip_level_; + unsigned int side_; + TextureGL* texture_; + DISALLOW_COPY_AND_ASSIGN(RenderSurfaceGL); +}; + +class RenderDepthStencilSurfaceGL : public RenderDepthStencilSurface { + public: + RenderDepthStencilSurfaceGL(int width, + int height); + virtual ~RenderDepthStencilSurfaceGL() {} + + static RenderDepthStencilSurfaceGL* Create( + int width, + int height); + + GLuint depth_buffer() const { + return render_buffers_[0]; + } + + GLuint stencil_buffer() const { + return render_buffers_[1]; + } + + private: + // Handles to the depth and stencil render-buffers, respectively. + GLuint render_buffers_[2]; + unsigned int width_; + unsigned int height_; + DISALLOW_COPY_AND_ASSIGN(RenderDepthStencilSurfaceGL); +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_WIN_GL_RENDER_SURFACE_GL_H__ + diff --git a/o3d/gpu/command_buffer/service/resource.cc b/o3d/gpu/command_buffer/service/resource.cc new file mode 100644 index 0000000..1208d30 --- /dev/null +++ b/o3d/gpu/command_buffer/service/resource.cc @@ -0,0 +1,101 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of ResourceMapBase. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/resource.h" + +namespace command_buffer { + +// Assigns a resource to a resource ID, by setting it at the right location +// into the list, resizing the list if necessary, and destroying an existing +// resource if one existed already. +void ResourceMapBase::Assign(ResourceId id, Resource *resource) { + if (id >= resources_.size()) { + resources_.resize(id + 1, NULL); + } else { + Resource *&entry = resources_[id]; + if (entry) { + delete entry; + entry = NULL; + } + } + DCHECK(resources_[id] == NULL); + resources_[id] = resource; +} + +// Destroys a resource contained in the map, setting its entry to NULL. If +// necessary, this will trim the list. +bool ResourceMapBase::Destroy(ResourceId id) { + if (id >= resources_.size()) { + return false; + } + Resource *&entry = resources_[id]; + if (entry) { + delete entry; + entry = NULL; + + // Removing the last element, we can trim the list. + // TODO: this may not be optimal to do every time. Investigate if it + // becomes an issue, and add a threshold before we resize. + if (id == resources_.size() - 1) { + size_t last_valid = resources_.max_size(); + for (unsigned int i = id; i < resources_.size(); --i) { + if (resources_[i]) { + last_valid = i; + break; + } + } + if (last_valid == resources_.max_size()) { + resources_.clear(); + } else { + resources_.resize(last_valid + 1); + } + } + return true; + } + return false; +} + +// Goes over all non-NULL entries in the list, destroying them, then clears the +// list. +void ResourceMapBase::DestroyAllResources() { + for (Container::iterator i = resources_.begin(); i != resources_.end(); ++i) { + if (*i) { + delete *i; + } + } + resources_.clear(); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/resource.h b/o3d/gpu/command_buffer/service/resource.h new file mode 100644 index 0000000..20e3038 --- /dev/null +++ b/o3d/gpu/command_buffer/service/resource.h @@ -0,0 +1,268 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition for resource classes and the resource map. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_RESOURCE_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_RESOURCE_H_ + +#include <vector> +#include "base/scoped_ptr.h" +#include "gpu/command_buffer/common/resource.h" + +namespace command_buffer { + +// Base class for resources, just providing a common Destroy function. +class Resource { + public: + Resource() {} + virtual ~Resource() {} + private: + DISALLOW_COPY_AND_ASSIGN(Resource); +}; + +// VertexBuffer class, representing a vertex buffer resource. +class VertexBuffer: public Resource { + public: + VertexBuffer(unsigned int size, unsigned int flags) + : size_(size), + flags_(flags) {} + virtual ~VertexBuffer() {} + + // Returns the vertex buffer flags. + unsigned int flags() const { return flags_; } + // Sets the vertex buffer flags. + void set_flags(unsigned int flags) { flags_ = flags; } + // Returns the vertex buffer size. + unsigned int size() const { return size_; } + // Sets the vertex buffer size. + void set_size(unsigned int size) { size_ = size; } + protected: + unsigned int size_; + unsigned int flags_; + private: + DISALLOW_COPY_AND_ASSIGN(VertexBuffer); +}; + +// IndexBuffer class, representing an index buffer resource. +class IndexBuffer: public Resource { + public: + IndexBuffer(unsigned int size, unsigned int flags) + : size_(size), + flags_(flags) {} + virtual ~IndexBuffer() {} + + // Returns the index buffer flags. + unsigned int flags() const { return flags_; } + // Sets the index buffer flags. + void set_flags(unsigned int flags) { flags_ = flags; } + // Returns the index buffer size. + unsigned int size() const { return size_; } + // Sets the index buffer size. + void set_size(unsigned int size) { size_ = size; } + protected: + unsigned int size_; + unsigned int flags_; + private: + DISALLOW_COPY_AND_ASSIGN(IndexBuffer); +}; + +// VertexStruct class, representing a vertex struct resource. +class VertexStruct: public Resource { + public: + // The representation of an input data stream. + struct Element { + ResourceId vertex_buffer; + unsigned int offset; + unsigned int stride; + vertex_struct::Type type; + vertex_struct::Semantic semantic; + unsigned int semantic_index; + }; + + explicit VertexStruct(unsigned int count) + : count_(count), + elements_(new Element[count]) { + memset(elements_.get(), 0, count * sizeof(Element)); // NOLINT + } + + // Returns the number of inputs in this struct. + unsigned int count() const { return count_; } + // Returns an element by index. + Element &GetElement(unsigned int i) { + DCHECK_GT(count_, i); + return elements_[i]; + } + protected: + unsigned int count_; + scoped_array<Element> elements_; + private: + DISALLOW_COPY_AND_ASSIGN(VertexStruct); +}; + +// Effect class, representing an effect. +class Effect: public Resource { + public: + Effect() {} + private: + DISALLOW_COPY_AND_ASSIGN(Effect); +}; + +// EffectParam class, representing an effect parameter. +class EffectParam: public Resource { + public: + explicit EffectParam(effect_param::DataType data_type) + : data_type_(data_type) { + } + + // Gets the data type of this parameter. + effect_param::DataType data_type() const { return data_type_; } + private: + effect_param::DataType data_type_; + DISALLOW_COPY_AND_ASSIGN(EffectParam); +}; + +// Texture class, representing a texture resource. +class Texture: public Resource { + public: + Texture(texture::Type type, + unsigned int levels, + texture::Format format, + bool enable_render_surfaces, + unsigned int flags) + : type_(type), + levels_(levels), + format_(format), + render_surfaces_enabled_(enable_render_surfaces), + flags_(flags) {} + virtual ~Texture() {} + + // Returns the type of the texture. + texture::Type type() const { return type_; } + // Returns the texture flags. + unsigned int flags() const { return flags_; } + // Returns the texture format. + texture::Format format() const { return format_; } + // Returns whether the texture supports render surfaces + bool render_surfaces_enabled() const { return render_surfaces_enabled_; } + // Returns the number of mipmap levels in the texture. + unsigned int levels() const { return levels_; } + private: + texture::Type type_; + unsigned int levels_; + texture::Format format_; + bool render_surfaces_enabled_; + unsigned int flags_; + DISALLOW_COPY_AND_ASSIGN(Texture); +}; + +// RenderSurface class, representing a render surface/target +class RenderSurface: public Resource { + public: + RenderSurface() {} + private: + DISALLOW_COPY_AND_ASSIGN(RenderSurface); +}; + +// RenderSurface class, representing a render surface/target +class RenderDepthStencilSurface: public Resource { + public: + RenderDepthStencilSurface() {} + private: + DISALLOW_COPY_AND_ASSIGN(RenderDepthStencilSurface); +}; + + +// Texture class, representing a sampler resource. +class Sampler: public Resource { + public: + Sampler() {} + private: + DISALLOW_COPY_AND_ASSIGN(Sampler); +}; + +// Base of ResourceMap. Contains most of the implementation of ResourceMap, to +// avoid template bloat. +class ResourceMapBase { + public: + ResourceMapBase() : resources_() {} + ~ResourceMapBase() {} + + // Assigns a resource to a resource ID. Assigning a resource to an ID that + // already has an existing resource will destroy that existing resource. The + // map takes ownership of the resource. + void Assign(ResourceId id, Resource* resource); + // Destroys a resource. + bool Destroy(ResourceId id); + // Destroy all resources. + void DestroyAllResources(); + // Gets a resource by ID. + Resource *Get(ResourceId id) { + return (id < resources_.size()) ? resources_[id] : NULL; + } + private: + typedef std::vector<Resource *> Container; + Container resources_; +}; + +// Resource Map class, allowing resource ID <-> Resource association. This is a +// dense map, optimized for retrieval (O(1)). +template<class T> class ResourceMap { + public: + ResourceMap() : container_() {} + ~ResourceMap() {} + + // Assigns a resource to a resource ID. Assigning a resource to an ID that + // already has an existing resource will destroy that existing resource. The + // map takes ownership of the resource. + void Assign(ResourceId id, T* resource) { + container_.Assign(id, resource); + } + // Destroys a resource. + bool Destroy(ResourceId id) { + return container_.Destroy(id); + } + // Destroy all resources. + void DestroyAllResources() { + return container_.DestroyAllResources(); + } + // Gets a resource by ID. + T *Get(ResourceId id) { + return static_cast<T*>(container_.Get(id)); + } + private: + ResourceMapBase container_; +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_RESOURCE_H_ diff --git a/o3d/gpu/command_buffer/service/resource_test.cc b/o3d/gpu/command_buffer/service/resource_test.cc new file mode 100644 index 0000000..3e95dfb --- /dev/null +++ b/o3d/gpu/command_buffer/service/resource_test.cc @@ -0,0 +1,127 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// Tests for the ResourceMap. + +#include "gpu/command_buffer/service/precompile.h" +#include "tests/common/win/testing_common.h" +#include "gpu/command_buffer/service/resource.h" + +namespace command_buffer { + +// Mock resource implementation that checks for leaks. +class ResourceMock : public Resource { + public: + ResourceMock() : Resource() { + ++instance_count_; + } + virtual ~ResourceMock() { + --instance_count_; + } + + // Returns the instance count. The instance count is increased in the + // constructor and decreased in the destructor, to track leaks. The reason is + // that we can't mock the destructor, though we want to make sure the mock is + // destroyed. + static int instance_count() { return instance_count_; } + private: + static int instance_count_; + DISALLOW_COPY_AND_ASSIGN(ResourceMock); +}; +int ResourceMock::instance_count_ = 0; + +// Test fixture for ResourceMap test. Creates a ResourceMap using a mock +// Resource, and checks for ResourceMock leaks. +class ResourceMapTest : public testing::Test { + protected: + typedef ResourceMap<ResourceMock> Map; + virtual void SetUp() { + instance_count_ = ResourceMock::instance_count(); + map_.reset(new Map()); + } + virtual void TearDown() { + CheckLeaks(); + } + + // Makes sure we didn't leak any ResourceMock object. + void CheckLeaks() { + EXPECT_EQ(instance_count_, ResourceMock::instance_count()); + } + + Map *map() const { return map_.get(); } + private: + int instance_count_; + scoped_ptr<Map> map_; +}; + +TEST_F(ResourceMapTest, TestMap) { + // check that initial mapping is empty. + EXPECT_EQ(NULL, map()->Get(0)); + EXPECT_EQ(NULL, map()->Get(1)); + EXPECT_EQ(NULL, map()->Get(392)); + + // create a new resource, assign it to an ID. + ResourceMock *resource = new ResourceMock(); + map()->Assign(123, resource); + EXPECT_EQ(resource, map()->Get(123)); + + // Destroy the resource, making sure the object is deleted. + EXPECT_EQ(true, map()->Destroy(123)); + EXPECT_EQ(false, map()->Destroy(123)); // destroying again should fail. + resource = NULL; + CheckLeaks(); + + // create a new resource, add it to the map, and make sure it gets deleted + // when we assign a new resource to that ID. + resource = new ResourceMock(); + map()->Assign(1, resource); + resource = new ResourceMock(); + map()->Assign(1, resource); + EXPECT_EQ(resource, map()->Get(1)); // check that we have the new resource. + EXPECT_EQ(true, map()->Destroy(1)); + CheckLeaks(); + + // Adds 3 resources, then call DestroyAllResources(). + resource = new ResourceMock(); + map()->Assign(1, resource); + resource = new ResourceMock(); + map()->Assign(2, resource); + resource = new ResourceMock(); + map()->Assign(3, resource); + map()->DestroyAllResources(); + EXPECT_EQ(NULL, map()->Get(1)); + EXPECT_EQ(NULL, map()->Get(2)); + EXPECT_EQ(NULL, map()->Get(3)); + CheckLeaks(); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/sampler_d3d9.cc b/o3d/gpu/command_buffer/service/sampler_d3d9.cc new file mode 100644 index 0000000..e4c3b26 --- /dev/null +++ b/o3d/gpu/command_buffer/service/sampler_d3d9.cc @@ -0,0 +1,199 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the SamplerD3D9 class. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/gapi_d3d9.h" +#include "gpu/command_buffer/service/sampler_d3d9.h" +#include "gpu/command_buffer/service/texture_d3d9.h" + +namespace command_buffer { +namespace o3d { + +namespace { + +// Converts an addressing mode to corresponding D3D values. +D3DTEXTUREADDRESS AddressModeToD3D(sampler::AddressingMode mode) { + switch (mode) { + case sampler::kWrap: + return D3DTADDRESS_WRAP; + case sampler::kMirrorRepeat: + return D3DTADDRESS_MIRROR; + case sampler::kClampToEdge: + return D3DTADDRESS_CLAMP; + case sampler::kClampToBorder: + return D3DTADDRESS_BORDER; + } + DLOG(FATAL) << "Not reached"; + return D3DTADDRESS_WRAP; +} + +// Converts a filtering mode to corresponding D3D values. +D3DTEXTUREFILTERTYPE FilteringModeToD3D(sampler::FilteringMode mode) { + switch (mode) { + case sampler::kNone: + return D3DTEXF_NONE; + case sampler::kPoint: + return D3DTEXF_POINT; + case sampler::kLinear: + return D3DTEXF_LINEAR; + } + DLOG(FATAL) << "Not reached"; + return D3DTEXF_POINT; +} + +} // anonymous namespace + +SamplerD3D9::SamplerD3D9() + : texture_id_(kInvalidResource) { + SetStates(sampler::kClampToEdge, + sampler::kClampToEdge, + sampler::kClampToEdge, + sampler::kLinear, + sampler::kLinear, + sampler::kPoint, + 1); + RGBA black = {0, 0, 0, 1}; + SetBorderColor(black); +} + +bool SamplerD3D9::ApplyStates(GAPID3D9 *gapi, unsigned int unit) const { + DCHECK(gapi); + TextureD3D9 *texture = gapi->GetTexture(texture_id_); + if (!texture) { + return false; + } + IDirect3DDevice9 *d3d_device = gapi->d3d_device(); + HR(d3d_device->SetTexture(unit, texture->d3d_base_texture())); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_ADDRESSU, d3d_address_u_)); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_ADDRESSV, d3d_address_v_)); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_ADDRESSW, d3d_address_w_)); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_MAGFILTER, d3d_mag_filter_)); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_MINFILTER, d3d_min_filter_)); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_MIPFILTER, d3d_mip_filter_)); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_MAXANISOTROPY, + d3d_max_anisotropy_)); + HR(d3d_device->SetSamplerState(unit, D3DSAMP_BORDERCOLOR, d3d_border_color_)); + return true; +} + +void SamplerD3D9::SetStates(sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy) { + // These are validated in GAPIDecoder.cc + DCHECK_NE(mag_filter, sampler::kNone); + DCHECK_NE(min_filter, sampler::kNone); + DCHECK_GT(max_anisotropy, 0U); + d3d_address_u_ = AddressModeToD3D(addressing_u); + d3d_address_v_ = AddressModeToD3D(addressing_v); + d3d_address_w_ = AddressModeToD3D(addressing_w); + d3d_mag_filter_ = FilteringModeToD3D(mag_filter); + d3d_min_filter_ = FilteringModeToD3D(min_filter); + d3d_mip_filter_ = FilteringModeToD3D(mip_filter); + if (max_anisotropy > 1) { + d3d_mag_filter_ = D3DTEXF_ANISOTROPIC; + d3d_min_filter_ = D3DTEXF_ANISOTROPIC; + } + d3d_max_anisotropy_ = max_anisotropy; +} + +void SamplerD3D9::SetBorderColor(const RGBA &color) { + d3d_border_color_ = RGBAToD3DCOLOR(color); +} + +parse_error::ParseError GAPID3D9::CreateSampler( + ResourceId id) { + // Dirty effect, because this sampler id may be used + DirtyEffect(); + samplers_.Assign(id, new SamplerD3D9()); + return parse_error::kParseNoError; +} + +// Destroys the Sampler resource. +parse_error::ParseError GAPID3D9::DestroySampler(ResourceId id) { + // Dirty effect, because this sampler id may be used + DirtyEffect(); + return samplers_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPID3D9::SetSamplerStates( + ResourceId id, + sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy) { + SamplerD3D9 *sampler = samplers_.Get(id); + if (!sampler) + return parse_error::kParseInvalidArguments; + // Dirty effect, because this sampler id may be used + DirtyEffect(); + sampler->SetStates(addressing_u, addressing_v, addressing_w, + mag_filter, min_filter, mip_filter, max_anisotropy); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::SetSamplerBorderColor( + ResourceId id, + const RGBA &color) { + SamplerD3D9 *sampler = samplers_.Get(id); + if (!sampler) + return parse_error::kParseInvalidArguments; + // Dirty effect, because this sampler id may be used + DirtyEffect(); + sampler->SetBorderColor(color); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPID3D9::SetSamplerTexture( + ResourceId id, + ResourceId texture_id) { + SamplerD3D9 *sampler = samplers_.Get(id); + if (!sampler) + return parse_error::kParseInvalidArguments; + // Dirty effect, because this sampler id may be used + DirtyEffect(); + sampler->SetTexture(texture_id); + return parse_error::kParseNoError; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/sampler_d3d9.h b/o3d/gpu/command_buffer/service/sampler_d3d9.h new file mode 100644 index 0000000..55a6e9a --- /dev/null +++ b/o3d/gpu/command_buffer/service/sampler_d3d9.h @@ -0,0 +1,85 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the SamplerD3D9 class, implementing +// samplers for D3D. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_SAMPLER_D3D9_H_ +#define GPU_COMMAND_BUFFER_SERVICE_SAMPLER_D3D9_H_ + +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/resource.h" + +namespace command_buffer { +namespace o3d { + +class GAPID3D9; + +// D3D9 version of Sampler. +class SamplerD3D9 : public Sampler { + public: + SamplerD3D9(); + + // Applies sampler states to D3D. + bool ApplyStates(GAPID3D9 *gapi, unsigned int unit) const; + + // Sets sampler states. + void SetStates(sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy); + + // Sets the border color states. + void SetBorderColor(const RGBA &color); + + // Sets the texture. + void SetTexture(ResourceId texture) { texture_id_ = texture; } + private: + D3DTEXTUREADDRESS d3d_address_u_; + D3DTEXTUREADDRESS d3d_address_v_; + D3DTEXTUREADDRESS d3d_address_w_; + D3DTEXTUREFILTERTYPE d3d_mag_filter_; + D3DTEXTUREFILTERTYPE d3d_min_filter_; + D3DTEXTUREFILTERTYPE d3d_mip_filter_; + DWORD d3d_max_anisotropy_; + D3DCOLOR d3d_border_color_; + ResourceId texture_id_; +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_SAMPLER_D3D9_H_ diff --git a/o3d/gpu/command_buffer/service/sampler_gl.cc b/o3d/gpu/command_buffer/service/sampler_gl.cc new file mode 100644 index 0000000..32335e1 --- /dev/null +++ b/o3d/gpu/command_buffer/service/sampler_gl.cc @@ -0,0 +1,238 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the sampler-related GAPI functions on GL. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/gapi_gl.h" +#include "gpu/command_buffer/service/sampler_gl.h" + +namespace command_buffer { +namespace o3d { + +namespace { + +// Gets the GL enum corresponding to an addressing mode. +GLenum GLAddressMode(sampler::AddressingMode o3d_mode) { + switch (o3d_mode) { + case sampler::kWrap: + return GL_REPEAT; + case sampler::kMirrorRepeat: + return GL_MIRRORED_REPEAT; + case sampler::kClampToEdge: + return GL_CLAMP_TO_EDGE; + case sampler::kClampToBorder: + return GL_CLAMP_TO_BORDER; + default: + DLOG(FATAL) << "Not Reached"; + return GL_REPEAT; + } +} + +// Gets the GL enum for the minification filter based on the command buffer min +// and mip filtering modes. +GLenum GLMinFilter(sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter) { + switch (min_filter) { + case sampler::kPoint: + if (mip_filter == sampler::kNone) + return GL_NEAREST; + else if (mip_filter == sampler::kPoint) + return GL_NEAREST_MIPMAP_NEAREST; + else if (mip_filter == sampler::kLinear) + return GL_NEAREST_MIPMAP_LINEAR; + case sampler::kLinear: + if (mip_filter == sampler::kNone) + return GL_LINEAR; + else if (mip_filter == sampler::kPoint) + return GL_LINEAR_MIPMAP_NEAREST; + else if (mip_filter == sampler::kLinear) + return GL_LINEAR_MIPMAP_LINEAR; + default: + DLOG(FATAL) << "Not Reached"; + return GL_LINEAR_MIPMAP_NEAREST; + } +} + +// Gets the GL enum for the magnification filter based on the command buffer mag +// filtering mode. +GLenum GLMagFilter(sampler::FilteringMode mag_filter) { + switch (mag_filter) { + case sampler::kPoint: + return GL_NEAREST; + case sampler::kLinear: + return GL_LINEAR; + default: + DLOG(FATAL) << "Not Reached"; + return GL_LINEAR; + } +} + +// Gets the GL enum representing the GL target based on the texture type. +GLenum GLTextureTarget(texture::Type type) { + switch (type) { + case texture::kTexture2d: + return GL_TEXTURE_2D; + case texture::kTexture3d: + return GL_TEXTURE_3D; + case texture::kTextureCube: + return GL_TEXTURE_CUBE_MAP; + default: + DLOG(FATAL) << "Not Reached"; + return GL_TEXTURE_2D; + } +} + +} // anonymous namespace + +SamplerGL::SamplerGL() + : texture_id_(kInvalidResource), + gl_texture_(0) { + SetStates(sampler::kClampToEdge, + sampler::kClampToEdge, + sampler::kClampToEdge, + sampler::kLinear, + sampler::kLinear, + sampler::kPoint, + 1); + RGBA black = {0, 0, 0, 1}; + SetBorderColor(black); +} + +bool SamplerGL::ApplyStates(GAPIGL *gapi) { + DCHECK(gapi); + TextureGL *texture = gapi->GetTexture(texture_id_); + if (!texture) { + gl_texture_ = 0; + return false; + } + GLenum target = GLTextureTarget(texture->type()); + gl_texture_ = texture->gl_texture(); + glBindTexture(target, gl_texture_); + glTexParameteri(target, GL_TEXTURE_WRAP_S, gl_wrap_s_); + glTexParameteri(target, GL_TEXTURE_WRAP_T, gl_wrap_t_); + glTexParameteri(target, GL_TEXTURE_WRAP_R, gl_wrap_r_); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, gl_min_filter_); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, gl_mag_filter_); + glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy_); + glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, gl_border_color_); + return true; +} + +void SamplerGL::SetStates(sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy) { + // These are validated in GAPIDecoder.cc + DCHECK_NE(mag_filter, sampler::kNone); + DCHECK_NE(min_filter, sampler::kNone); + DCHECK_GT(max_anisotropy, 0U); + gl_wrap_s_ = GLAddressMode(addressing_u); + gl_wrap_t_ = GLAddressMode(addressing_v); + gl_wrap_r_ = GLAddressMode(addressing_w); + gl_mag_filter_ = GLMagFilter(mag_filter); + gl_min_filter_ = GLMinFilter(min_filter, mip_filter); + gl_max_anisotropy_ = max_anisotropy; +} + +void SamplerGL::SetBorderColor(const RGBA &color) { + gl_border_color_[0] = color.red; + gl_border_color_[1] = color.green; + gl_border_color_[2] = color.blue; + gl_border_color_[3] = color.alpha; +} + +parse_error::ParseError GAPIGL::CreateSampler( + ResourceId id) { + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + samplers_.Assign(id, new SamplerGL()); + return parse_error::kParseNoError; +} + +// Destroys the Sampler resource. +parse_error::ParseError GAPIGL::DestroySampler(ResourceId id) { + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + return samplers_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +parse_error::ParseError GAPIGL::SetSamplerStates( + ResourceId id, + sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy) { + SamplerGL *sampler = samplers_.Get(id); + if (!sampler) + return parse_error::kParseInvalidArguments; + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + sampler->SetStates(addressing_u, addressing_v, addressing_w, + mag_filter, min_filter, mip_filter, max_anisotropy); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::SetSamplerBorderColor( + ResourceId id, + const RGBA &color) { + SamplerGL *sampler = samplers_.Get(id); + if (!sampler) + return parse_error::kParseInvalidArguments; + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + sampler->SetBorderColor(color); + return parse_error::kParseNoError; +} + +parse_error::ParseError GAPIGL::SetSamplerTexture( + ResourceId id, + ResourceId texture_id) { + SamplerGL *sampler = samplers_.Get(id); + if (!sampler) + return parse_error::kParseInvalidArguments; + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + sampler->SetTexture(texture_id); + return parse_error::kParseNoError; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/sampler_gl.h b/o3d/gpu/command_buffer/service/sampler_gl.h new file mode 100644 index 0000000..8809ea5 --- /dev/null +++ b/o3d/gpu/command_buffer/service/sampler_gl.h @@ -0,0 +1,87 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file declares the SamplerGL class. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_SAMPLER_GL_H_ +#define GPU_COMMAND_BUFFER_SERVICE_SAMPLER_GL_H_ + +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/gl_utils.h" +#include "gpu/command_buffer/service/resource.h" + +namespace command_buffer { +namespace o3d { + +class GAPIGL; + +// GL version of Sampler. +class SamplerGL : public Sampler { + public: + SamplerGL(); + + // Applies sampler states to GL. + bool ApplyStates(GAPIGL *gapi); + + // Sets sampler states. + void SetStates(sampler::AddressingMode addressing_u, + sampler::AddressingMode addressing_v, + sampler::AddressingMode addressing_w, + sampler::FilteringMode mag_filter, + sampler::FilteringMode min_filter, + sampler::FilteringMode mip_filter, + unsigned int max_anisotropy); + + // Sets the border color states. + void SetBorderColor(const o3d::RGBA &color); + + // Sets the texture. + void SetTexture(ResourceId texture) { texture_id_ = texture; } + + GLuint gl_texture() const { return gl_texture_; } + + private: + GLenum gl_wrap_s_; + GLenum gl_wrap_t_; + GLenum gl_wrap_r_; + GLenum gl_mag_filter_; + GLenum gl_min_filter_; + GLuint gl_max_anisotropy_; + GLfloat gl_border_color_[4]; + GLuint gl_texture_; + ResourceId texture_id_; +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_SAMPLER_GL_H_ diff --git a/o3d/gpu/command_buffer/service/states_d3d9.cc b/o3d/gpu/command_buffer/service/states_d3d9.cc new file mode 100644 index 0000000..7abcada --- /dev/null +++ b/o3d/gpu/command_buffer/service/states_d3d9.cc @@ -0,0 +1,355 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of the state-related GAPID3D9 +// functions. + +#include "gpu/command_buffer/service/precompile.h" + +#include <algorithm> + +#include "gpu/command_buffer/common/o3d_cmd_format.h" +#include "gpu/command_buffer/service/gapi_d3d9.h" + +namespace command_buffer { +namespace o3d { + +namespace { + +// Checks that a command_buffer enum matches a D3D enum so that it can be +// converted quickly. +#define CHECK_CB_ENUM_MATCHES_D3D(CB_ENUM, D3D_ENUM) \ + COMPILE_ASSERT(CB_ENUM + 1 == D3D_ENUM, \ + CB_ENUM ## _plus_1_not_ ## D3D_ENUM) + +// Converts values from the PolygonMode enum to corresponding D3D values +inline D3DFILLMODE PolygonModeToD3D(PolygonMode fill_mode) { + DCHECK_LT(fill_mode, kNumPolygonMode); + + // Check that all acceptable values translate to D3D values by adding 1. + + CHECK_CB_ENUM_MATCHES_D3D(kPolygonModePoints, D3DFILL_POINT); + CHECK_CB_ENUM_MATCHES_D3D(kPolygonModeLines, D3DFILL_WIREFRAME); + CHECK_CB_ENUM_MATCHES_D3D(kPolygonModeFill, D3DFILL_SOLID); + return static_cast<D3DFILLMODE>(fill_mode + 1); +} + +// Converts values from the FaceCullMode enum to corresponding D3D values +inline D3DCULL FaceCullModeToD3D(FaceCullMode cull_mode) { + DCHECK_LT(cull_mode, kNumFaceCullMode); + + // Check that all acceptable values translate to D3D values by adding 1. + CHECK_CB_ENUM_MATCHES_D3D(kCullNone, D3DCULL_NONE); + CHECK_CB_ENUM_MATCHES_D3D(kCullCW, D3DCULL_CW); + CHECK_CB_ENUM_MATCHES_D3D(kCullCCW, D3DCULL_CCW); + return static_cast<D3DCULL>(cull_mode + 1); +} + +// Converts values from the Comparison enum to corresponding D3D values +inline D3DCMPFUNC ComparisonToD3D(Comparison comp) { + DCHECK_LT(comp, kNumComparison); + + // Check that all acceptable values translate to D3D values by adding 1. + CHECK_CB_ENUM_MATCHES_D3D(kNever, D3DCMP_NEVER); + CHECK_CB_ENUM_MATCHES_D3D(kLess, D3DCMP_LESS); + CHECK_CB_ENUM_MATCHES_D3D(kEqual, D3DCMP_EQUAL); + CHECK_CB_ENUM_MATCHES_D3D(kLEqual, D3DCMP_LESSEQUAL); + CHECK_CB_ENUM_MATCHES_D3D(kGreater, D3DCMP_GREATER); + CHECK_CB_ENUM_MATCHES_D3D(kNotEqual, D3DCMP_NOTEQUAL); + CHECK_CB_ENUM_MATCHES_D3D(kGEqual, D3DCMP_GREATEREQUAL); + CHECK_CB_ENUM_MATCHES_D3D(kAlways, D3DCMP_ALWAYS); + return static_cast<D3DCMPFUNC>(comp + 1); +} + +// Converts values from the StencilOp enum to corresponding D3D values +inline D3DSTENCILOP StencilOpToD3D(StencilOp stencil_op) { + DCHECK_LT(stencil_op, kNumStencilOp); + + // Check that all acceptable values translate to D3D values by adding 1. + CHECK_CB_ENUM_MATCHES_D3D(kKeep, D3DSTENCILOP_KEEP); + CHECK_CB_ENUM_MATCHES_D3D(kZero, D3DSTENCILOP_ZERO); + CHECK_CB_ENUM_MATCHES_D3D(kReplace, D3DSTENCILOP_REPLACE); + CHECK_CB_ENUM_MATCHES_D3D(kIncNoWrap, D3DSTENCILOP_INCRSAT); + CHECK_CB_ENUM_MATCHES_D3D(kDecNoWrap, D3DSTENCILOP_DECRSAT); + CHECK_CB_ENUM_MATCHES_D3D(kInvert, D3DSTENCILOP_INVERT); + CHECK_CB_ENUM_MATCHES_D3D(kIncWrap, D3DSTENCILOP_INCR); + CHECK_CB_ENUM_MATCHES_D3D(kDecWrap, D3DSTENCILOP_DECR); + return static_cast<D3DSTENCILOP>(stencil_op + 1); +} + +// Converts values from the BlendEq enum to corresponding D3D values +inline D3DBLENDOP BlendEqToD3D(BlendEq blend_eq) { + DCHECK_LT(blend_eq, kNumBlendEq); + // Check that all acceptable values translate to D3D values by adding 1. + CHECK_CB_ENUM_MATCHES_D3D(kBlendEqAdd, D3DBLENDOP_ADD); + CHECK_CB_ENUM_MATCHES_D3D(kBlendEqSub, D3DBLENDOP_SUBTRACT); + CHECK_CB_ENUM_MATCHES_D3D(kBlendEqRevSub, D3DBLENDOP_REVSUBTRACT); + CHECK_CB_ENUM_MATCHES_D3D(kBlendEqMin, D3DBLENDOP_MIN); + CHECK_CB_ENUM_MATCHES_D3D(kBlendEqMax, D3DBLENDOP_MAX); + return static_cast<D3DBLENDOP>(blend_eq + 1); +} + +// Converts values from the BlendFunc enum to corresponding D3D values +D3DBLEND BlendFuncToD3D(BlendFunc blend_func) { + // The D3DBLEND enum values don't map 1-to-1 to BlendFunc, so we use a switch + // here. + switch (blend_func) { + case kBlendFuncZero: + return D3DBLEND_ZERO; + case kBlendFuncOne: + return D3DBLEND_ONE; + case kBlendFuncSrcColor: + return D3DBLEND_SRCCOLOR; + case kBlendFuncInvSrcColor: + return D3DBLEND_INVSRCCOLOR; + case kBlendFuncSrcAlpha: + return D3DBLEND_SRCALPHA; + case kBlendFuncInvSrcAlpha: + return D3DBLEND_INVSRCALPHA; + case kBlendFuncDstAlpha: + return D3DBLEND_DESTALPHA; + case kBlendFuncInvDstAlpha: + return D3DBLEND_INVDESTALPHA; + case kBlendFuncDstColor: + return D3DBLEND_DESTCOLOR; + case kBlendFuncInvDstColor: + return D3DBLEND_INVDESTCOLOR; + case kBlendFuncSrcAlphaSaturate: + return D3DBLEND_SRCALPHASAT; + case kBlendFuncBlendColor: + return D3DBLEND_BLENDFACTOR; + case kBlendFuncInvBlendColor: + return D3DBLEND_INVBLENDFACTOR; + default: + DLOG(FATAL) << "Invalid BlendFunc"; + return D3DBLEND_ZERO; + } +} + +// Decodes stencil test function and operations from the bitfield. +void DecodeStencilFuncOps(Uint32 params, + Comparison *func, + StencilOp *pass, + StencilOp *fail, + StencilOp *zfail) { + // Sanity check. The value has already been tested in + // GAPIDecoder::DecodeSetStencilTest in gapi_decoder.cc. + DCHECK_EQ(SetStencilTest::Unused1::Get(params), 0); + // Check that the bitmask get cannot generate values outside of the allowed + // range. + COMPILE_ASSERT(SetStencilTest::CWFunc::kMask < + kNumComparison, + set_stencil_test_CWFunc_may_produce_invalid_values); + *func = static_cast<Comparison>(SetStencilTest::CWFunc::Get(params)); + + COMPILE_ASSERT(SetStencilTest::CWPassOp::kMask < + kNumStencilOp, + set_stencil_test_CWPassOp_may_produce_invalid_values); + *pass = static_cast<StencilOp>(SetStencilTest::CWPassOp::Get(params)); + + COMPILE_ASSERT(SetStencilTest::CWFailOp::kMask < + kNumStencilOp, + set_stencil_test_CWFailOp_may_produce_invalid_values); + *fail = static_cast<StencilOp>(SetStencilTest::CWFailOp::Get(params)); + + COMPILE_ASSERT(SetStencilTest::CWZFailOp::kMask < + kNumStencilOp, + set_stencil_test_CWZFailOp_may_produce_invalid_values); + *zfail = static_cast<StencilOp>(SetStencilTest::CWZFailOp::Get(params)); +} + +} // anonymous namespace + +void GAPID3D9::SetScissor(bool enable, + unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height) { + HR(d3d_device_->SetRenderState(D3DRS_SCISSORTESTENABLE, + enable ? TRUE : FALSE)); + RECT rect = {x, y, x + width, y + height}; + HR(d3d_device_->SetScissorRect(&rect)); +} + +void GAPID3D9::SetPolygonOffset(float slope_factor, float units) { + HR(d3d_device_->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, + FloatAsDWORD(slope_factor))); + // TODO: this value is hard-coded currently because we only create a + // 24-bit depth buffer. Move this to a member of GAPID3D9 if this changes. + const float kUnitScale = 1.f / (1 << 24); + HR(d3d_device_->SetRenderState(D3DRS_DEPTHBIAS, + FloatAsDWORD(units * kUnitScale))); +} + +void GAPID3D9::SetPointLineRaster(bool line_smooth, + bool point_sprite, + float point_size) { + HR(d3d_device_->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, + line_smooth ? TRUE : FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_POINTSPRITEENABLE, + point_sprite ? TRUE : FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_POINTSIZE, + FloatAsDWORD(point_size))); +} + +void GAPID3D9::SetPolygonRaster(PolygonMode fill_mode, + FaceCullMode cull_mode) { + HR(d3d_device_->SetRenderState(D3DRS_FILLMODE, PolygonModeToD3D(fill_mode))); + HR(d3d_device_->SetRenderState(D3DRS_CULLMODE, FaceCullModeToD3D(cull_mode))); +} + +void GAPID3D9::SetAlphaTest(bool enable, + float reference, + Comparison comp) { + HR(d3d_device_->SetRenderState(D3DRS_ALPHABLENDENABLE, + enable ? TRUE : FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_ALPHAREF, + FloatToClampedByte(reference))); + HR(d3d_device_->SetRenderState(D3DRS_ALPHAFUNC, ComparisonToD3D(comp))); +} + +void GAPID3D9::SetDepthTest(bool enable, + bool write_enable, + Comparison comp) { + HR(d3d_device_->SetRenderState(D3DRS_ZENABLE, + enable ? TRUE : FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_ZWRITEENABLE, + write_enable ? TRUE : FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_ZFUNC, ComparisonToD3D(comp))); +} + +void GAPID3D9::SetStencilTest(bool enable, + bool separate_ccw, + unsigned int write_mask, + unsigned int compare_mask, + unsigned int ref, + Uint32 func_ops) { + HR(d3d_device_->SetRenderState(D3DRS_STENCILENABLE, + enable ? TRUE : FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_STENCILWRITEMASK, write_mask)); + HR(d3d_device_->SetRenderState(D3DRS_STENCILMASK, compare_mask)); + HR(d3d_device_->SetRenderState(D3DRS_STENCILREF, ref)); + + Comparison func; + StencilOp pass; + StencilOp fail; + StencilOp zfail; + DecodeStencilFuncOps(func_ops, &func, &pass, &fail, &zfail); + HR(d3d_device_->SetRenderState(D3DRS_STENCILFUNC, + ComparisonToD3D(func))); + HR(d3d_device_->SetRenderState(D3DRS_STENCILPASS, + StencilOpToD3D(pass))); + HR(d3d_device_->SetRenderState(D3DRS_STENCILFAIL, + StencilOpToD3D(fail))); + HR(d3d_device_->SetRenderState(D3DRS_STENCILZFAIL, + StencilOpToD3D(zfail))); + + if (separate_ccw) { + HR(d3d_device_->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, TRUE)); + // Check that the definition of the counter-clockwise func/ops match the + // clockwise ones, just shifted by 16 bits, so that we can use + // DecodeStencilFuncOps on both of them. +#define CHECK_CCW_MATCHES_CW(FIELD) \ + COMPILE_ASSERT(SetStencilTest::CW ## FIELD::kLength == \ + SetStencilTest::CCW ## FIELD::kLength, \ + CCW ## FIELD ## _length_does_not_match_ ## CW ## FIELD); \ + COMPILE_ASSERT(SetStencilTest::CW ## FIELD::kShift + 16 == \ + SetStencilTest::CCW ## FIELD::kShift, \ + CCW ## FIELD ## _shift_does_not_match_ ## CW ## FIELD) + CHECK_CCW_MATCHES_CW(Func); + CHECK_CCW_MATCHES_CW(PassOp); + CHECK_CCW_MATCHES_CW(FailOp); + CHECK_CCW_MATCHES_CW(ZFailOp); +#undef CHECK_CCW_MATCHES_CW + // Extract upper 16 bits. + Uint32 ccw_func_ops = BitField<16, 16>::Get(func_ops); + + DecodeStencilFuncOps(ccw_func_ops, &func, &pass, &fail, &zfail); + HR(d3d_device_->SetRenderState(D3DRS_CCW_STENCILFUNC, + ComparisonToD3D(func))); + HR(d3d_device_->SetRenderState(D3DRS_CCW_STENCILPASS, + StencilOpToD3D(pass))); + HR(d3d_device_->SetRenderState(D3DRS_CCW_STENCILFAIL, + StencilOpToD3D(fail))); + HR(d3d_device_->SetRenderState(D3DRS_CCW_STENCILZFAIL, + StencilOpToD3D(zfail))); + } else { + HR(d3d_device_->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, FALSE)); + } +} + +void GAPID3D9::SetColorWrite(bool red, + bool green, + bool blue, + bool alpha, + bool dither) { + Uint32 mask = red ? D3DCOLORWRITEENABLE_RED : 0; + mask |= green ? D3DCOLORWRITEENABLE_GREEN : 0; + mask |= blue ? D3DCOLORWRITEENABLE_BLUE : 0; + mask |= alpha ? D3DCOLORWRITEENABLE_ALPHA : 0; + HR(d3d_device_->SetRenderState(D3DRS_COLORWRITEENABLE, mask)); + HR(d3d_device_->SetRenderState(D3DRS_DITHERENABLE, dither ? TRUE : FALSE)); +} + +void GAPID3D9::SetBlending(bool enable, + bool separate_alpha, + BlendEq color_eq, + BlendFunc color_src_func, + BlendFunc color_dst_func, + BlendEq alpha_eq, + BlendFunc alpha_src_func, + BlendFunc alpha_dst_func) { + HR(d3d_device_->SetRenderState(D3DRS_ALPHABLENDENABLE, + enable ? TRUE : FALSE)); + HR(d3d_device_->SetRenderState(D3DRS_BLENDOP, BlendEqToD3D(color_eq))); + HR(d3d_device_->SetRenderState(D3DRS_SRCBLEND, + BlendFuncToD3D(color_src_func))); + HR(d3d_device_->SetRenderState(D3DRS_DESTBLEND, + BlendFuncToD3D(color_dst_func))); + if (separate_alpha) { + HR(d3d_device_->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE)); + HR(d3d_device_->SetRenderState(D3DRS_BLENDOP, BlendEqToD3D(alpha_eq))); + HR(d3d_device_->SetRenderState(D3DRS_SRCBLEND, + BlendFuncToD3D(alpha_src_func))); + HR(d3d_device_->SetRenderState(D3DRS_DESTBLEND, + BlendFuncToD3D(alpha_dst_func))); + } else { + HR(d3d_device_->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE)); + } +} + +void GAPID3D9::SetBlendingColor(const RGBA &color) { + HR(d3d_device_->SetRenderState(D3DRS_BLENDFACTOR, RGBAToD3DCOLOR(color))); +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/states_gl.cc b/o3d/gpu/command_buffer/service/states_gl.cc new file mode 100644 index 0000000..2d6fce4 --- /dev/null +++ b/o3d/gpu/command_buffer/service/states_gl.cc @@ -0,0 +1,346 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the render state-related GAPI functions on GL. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/common/o3d_cmd_format.h" +#include "gpu/command_buffer/service/gapi_gl.h" + +namespace command_buffer { +namespace o3d { + +namespace { + +GLenum kGLPolygonModes[] = { + GL_POINT, + GL_LINE, + GL_FILL, +}; +COMPILE_ASSERT(o3d::kNumPolygonMode == arraysize(kGLPolygonModes), + kGLPolygonModes_does_not_match_command_buffer_PolygonMode); + +GLenum kGLComparison[] = { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, +}; +COMPILE_ASSERT(o3d::kNumComparison == arraysize(kGLComparison), + kGLComparison_does_not_match_command_buffer_Comparison); + +GLenum kGLBlendFunc[] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA_SATURATE, + GL_CONSTANT_COLOR, + GL_ONE_MINUS_CONSTANT_COLOR, +}; +COMPILE_ASSERT(o3d::kNumBlendFunc == arraysize(kGLBlendFunc), + kGLBlendFunc_does_not_match_command_buffer_BlendFunc); + +GLenum kGLBlendEq[] = { + GL_FUNC_ADD, + GL_FUNC_SUBTRACT, + GL_FUNC_REVERSE_SUBTRACT, + GL_MIN, + GL_MAX, +}; +COMPILE_ASSERT(o3d::kNumBlendEq == arraysize(kGLBlendEq), + kGLBlendEq_does_not_match_command_buffer_BlendEq); + +GLenum kGLStencilOp[] = { + GL_KEEP, + GL_ZERO, + GL_REPLACE, + GL_INCR, + GL_DECR, + GL_INVERT, + GL_INCR_WRAP, + GL_DECR_WRAP, +}; +COMPILE_ASSERT(o3d::kNumStencilOp == arraysize(kGLStencilOp), + kGLStencilOp_does_not_match_command_buffer_StencilOp); + +// Check that the definition of the counter-clockwise func/ops match the +// clockwise ones, just shifted by 16 bits, so that we can use +// DecodeStencilFuncOps on both of them. +#define CHECK_CCW_MATCHES_CW(FIELD) \ + COMPILE_ASSERT(o3d::SetStencilTest::CW ## FIELD::kLength == \ + o3d::SetStencilTest::CCW ## FIELD::kLength, \ + CCW ## FIELD ## _length_does_not_match_ ## CW ## FIELD); \ + COMPILE_ASSERT(o3d::SetStencilTest::CW ## FIELD::kShift + 16 == \ + o3d::SetStencilTest::CCW ## FIELD::kShift, \ + CCW ## FIELD ## _shift_does_not_match_ ## CW ## FIELD) + +CHECK_CCW_MATCHES_CW(Func); +CHECK_CCW_MATCHES_CW(PassOp); +CHECK_CCW_MATCHES_CW(FailOp); +CHECK_CCW_MATCHES_CW(ZFailOp); + +#undef CHECK_CCW_MATCHES_CW + +// Decodes stencil test function and operations from the bitfield. +void DecodeStencilFuncOps(Uint32 params, + GLenum *func, + GLenum *pass, + GLenum *fail, + GLenum *zfail) { + // Sanity check. The value has already been tested in + // GAPIDecoder::DecodeSetStencilTest in gapi_decoder.cc. + DCHECK_EQ(o3d::SetStencilTest::Unused1::Get(params), 0); + // Check that the bitmask get cannot generate values outside of the allowed + // range. + COMPILE_ASSERT(o3d::SetStencilTest::CWFunc::kMask < + o3d::kNumComparison, + set_stencil_test_CWFunc_may_produce_invalid_values); + *func = kGLComparison[o3d::SetStencilTest::CWFunc::Get(params)]; + + COMPILE_ASSERT(o3d::SetStencilTest::CWPassOp::kMask < + o3d::kNumStencilOp, + set_stencil_test_CWPassOp_may_produce_invalid_values); + *pass = kGLStencilOp[o3d::SetStencilTest::CWPassOp::Get(params)]; + + COMPILE_ASSERT(o3d::SetStencilTest::CWFailOp::kMask < + o3d::kNumStencilOp, + set_stencil_test_CWFailOp_may_produce_invalid_values); + *fail = kGLStencilOp[o3d::SetStencilTest::CWFailOp::Get(params)]; + + COMPILE_ASSERT(o3d::SetStencilTest::CWZFailOp::kMask < + o3d::kNumStencilOp, + set_stencil_test_CWZFailOp_may_produce_invalid_values); + *zfail = kGLStencilOp[o3d::SetStencilTest::CWZFailOp::Get(params)]; +} + +} // anonymous namespace + +void GAPIGL::SetViewport(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height, + float z_min, + float z_max) { + glViewport(x, y, width, height); + glDepthRange(z_min, z_max); + // Update the helper constant used for the D3D -> GL remapping. + // See effect_gl.cc for details. + glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 0, + 1.f / width, 1.f / height, 2.f, 0.f); + CHECK_GL_ERROR(); +} + +void GAPIGL::SetScissor(bool enable, + unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height) { + if (enable) { + glEnable(GL_SCISSOR_TEST); + glScissor(x, y, width, height); + } else { + glDisable(GL_SCISSOR_TEST); + } +} + +void GAPIGL::SetPointLineRaster(bool line_smooth, + bool point_sprite, + float point_size) { + if (line_smooth) { + glEnable(GL_LINE_SMOOTH); + } else { + glDisable(GL_LINE_SMOOTH); + } + if (point_sprite) { + glEnable(GL_POINT_SPRITE); + // TODO: check which TC gets affected by point sprites in D3D. + glActiveTextureARB(GL_TEXTURE0); + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + } else { + glActiveTextureARB(GL_TEXTURE0); + glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_FALSE); + glDisable(GL_POINT_SPRITE); + } + glPointSize(point_size); +} + +void GAPIGL::SetPolygonOffset(float slope_factor, float units) { + glPolygonOffset(slope_factor, units); +} + +void GAPIGL::SetPolygonRaster(o3d::PolygonMode fill_mode, + o3d::FaceCullMode cull_mode) { + DCHECK_LT(fill_mode, kNumPolygonMode); + glPolygonMode(GL_FRONT_AND_BACK, kGLPolygonModes[fill_mode]); + DCHECK_LT(cull_mode, kNumFaceCullMode); + switch (cull_mode) { + case kCullCW: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + break; + case kCullCCW: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + break; + default: + glDisable(GL_CULL_FACE); + break; + } +} + +void GAPIGL::SetAlphaTest(bool enable, + float reference, + o3d::Comparison comp) { + DCHECK_LT(comp, kNumComparison); + if (enable) { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(kGLComparison[comp], reference); + } else { + glDisable(GL_ALPHA_TEST); + } +} + +void GAPIGL::SetDepthTest(bool enable, + bool write_enable, + o3d::Comparison comp) { + DCHECK_LT(comp, kNumComparison); + if (enable) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(kGLComparison[comp]); + } else { + glDisable(GL_DEPTH_TEST); + } + glDepthMask(write_enable); +} + +void GAPIGL::SetStencilTest(bool enable, + bool separate_ccw, + unsigned int write_mask, + unsigned int compare_mask, + unsigned int ref, + Uint32 func_ops) { + if (enable) { + glEnable(GL_STENCIL_TEST); + glStencilMask(write_mask); + + GLenum func; + GLenum pass; + GLenum fail; + GLenum zfail; + DecodeStencilFuncOps(func_ops, &func, &pass, &fail, &zfail); + if (separate_ccw) { + glStencilFuncSeparate(GL_FRONT, func, ref, compare_mask); + glStencilOpSeparate(GL_FRONT, pass, fail, zfail); + // Extract upper 16 bits. + Uint32 ccw_func_ops = BitField<16, 16>::Get(func_ops); + GLenum ccw_func; + GLenum ccw_pass; + GLenum ccw_fail; + GLenum ccw_zfail; + DecodeStencilFuncOps(ccw_func_ops, &ccw_func, &ccw_pass, &ccw_fail, + &ccw_zfail); + glStencilFuncSeparate(GL_BACK, ccw_func, ref, compare_mask); + glStencilOpSeparate(GL_BACK, ccw_pass, ccw_fail, ccw_zfail); + } else { + glStencilFunc(func, ref, compare_mask); + glStencilOp(pass, fail, zfail); + } + } else { + glDisable(GL_STENCIL_TEST); + } +} + +void GAPIGL::SetColorWrite(bool red, + bool green, + bool blue, + bool alpha, + bool dither) { + glColorMask(red, green, blue, alpha); + if (dither) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } +} + +void GAPIGL::SetBlending(bool enable, + bool separate_alpha, + BlendEq color_eq, + BlendFunc color_src_func, + BlendFunc color_dst_func, + BlendEq alpha_eq, + BlendFunc alpha_src_func, + BlendFunc alpha_dst_func) { + DCHECK_LT(color_eq, kNumBlendEq); + DCHECK_LT(color_src_func, kNumBlendFunc); + DCHECK_LT(color_dst_func, kNumBlendFunc); + DCHECK_LT(alpha_eq, kNumBlendEq); + DCHECK_LT(alpha_src_func, kNumBlendFunc); + DCHECK_LT(alpha_dst_func, kNumBlendFunc); + if (enable) { + glEnable(GL_BLEND); + GLenum gl_color_eq = kGLBlendEq[color_eq]; + GLenum gl_color_src_func = kGLBlendFunc[color_src_func]; + GLenum gl_color_dst_func = kGLBlendFunc[color_dst_func]; + if (separate_alpha) { + GLenum gl_alpha_eq = kGLBlendEq[alpha_eq]; + GLenum gl_alpha_src_func = kGLBlendFunc[alpha_src_func]; + GLenum gl_alpha_dst_func = kGLBlendFunc[alpha_dst_func]; + glBlendFuncSeparate(gl_color_src_func, gl_color_dst_func, + gl_alpha_src_func, gl_alpha_dst_func); + glBlendEquationSeparate(gl_color_eq, gl_alpha_eq); + } else { + glBlendFunc(gl_color_src_func, gl_color_dst_func); + glBlendEquation(gl_color_eq); + } + } else { + glDisable(GL_BLEND); + } +} + +void GAPIGL::SetBlendingColor(const RGBA &color) { + glBlendColor(color.red, color.green, color.blue, color.alpha); +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/texture_d3d9.cc b/o3d/gpu/command_buffer/service/texture_d3d9.cc new file mode 100644 index 0000000..9f25ff0 --- /dev/null +++ b/o3d/gpu/command_buffer/service/texture_d3d9.cc @@ -0,0 +1,731 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the D3D9 versions of the texture resources, as well as +// the related GAPID3D9 function implementations. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/gapi_d3d9.h" +#include "gpu/command_buffer/service/texture_d3d9.h" + +namespace command_buffer { +namespace o3d { + +// Converts a texture format to a D3D texture format. +D3DFORMAT TextureD3D9::D3DFormat(texture::Format format) { + switch (format) { + case texture::kXRGB8: return D3DFMT_X8R8G8B8; + case texture::kARGB8: return D3DFMT_A8R8G8B8; + case texture::kABGR16F: return D3DFMT_A16B16G16R16F; + case texture::kR32F: return D3DFMT_R32F; + case texture::kABGR32F: return D3DFMT_A32B32G32R32F; + case texture::kDXT1: return D3DFMT_DXT1; + // TODO(petersont): Add DXT3/5 support. + default: return D3DFMT_UNKNOWN; + }; +} + +// Converts a cube map face to a D3D face. +D3DCUBEMAP_FACES TextureD3D9::D3DFace(texture::Face face) { + switch (face) { + case texture::kFacePositiveX: + return D3DCUBEMAP_FACE_POSITIVE_X; + case texture::kFaceNegativeX: + return D3DCUBEMAP_FACE_NEGATIVE_X; + case texture::kFacePositiveY: + return D3DCUBEMAP_FACE_POSITIVE_Y; + case texture::kFaceNegativeY: + return D3DCUBEMAP_FACE_NEGATIVE_Y; + case texture::kFacePositiveZ: + return D3DCUBEMAP_FACE_POSITIVE_Z; + case texture::kFaceNegativeZ: + return D3DCUBEMAP_FACE_NEGATIVE_Z; + } + LOG(FATAL) << "Not reached."; + return D3DCUBEMAP_FACE_POSITIVE_X; +} + +// Texture 2D functions + +// Destroys the 2D texture, releasing the D3D texture, and its shadow if any. +Texture2DD3D9::~Texture2DD3D9() { + DCHECK(d3d_texture_); + d3d_texture_ = NULL; + if (d3d_shadow_) { + d3d_shadow_->Release(); + d3d_shadow_ = NULL; + } +} + +// Creates a 2D texture. For dynamic textures, create it in the default pool, +// and a shadow version in the system memory pool (that we can lock). For +// regular texture, simply create one in the managed pool. +Texture2DD3D9 *Texture2DD3D9::Create(GAPID3D9 *gapi, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + DCHECK_GT(width, 0U); + DCHECK_GT(height, 0U); + DCHECK_GT(levels, 0U); + D3DFORMAT d3d_format = D3DFormat(format); + IDirect3DDevice9 *device = gapi->d3d_device(); + if (enable_render_surfaces) { + CComPtr<IDirect3DTexture9> d3d_texture = NULL; + HRESULT result = device->CreateTexture(width, height, levels, + D3DUSAGE_RENDERTARGET, d3d_format, + D3DPOOL_DEFAULT, &d3d_texture, + NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new Texture2DD3D9(levels, format, flags, width, height, d3d_texture, + NULL, enable_render_surfaces); + } else if (flags & texture::kDynamic) { + CComPtr<IDirect3DTexture9> d3d_texture = NULL; + HRESULT result = device->CreateTexture(width, height, levels, + D3DUSAGE_DYNAMIC, d3d_format, + D3DPOOL_DEFAULT, &d3d_texture, + NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + CComPtr<IDirect3DTexture9> d3d_shadow = NULL; + result = device->CreateTexture(width, height, levels, D3DUSAGE_DYNAMIC, + d3d_format, D3DPOOL_SYSTEMMEM, &d3d_shadow, + NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new Texture2DD3D9(levels, format, flags, width, height, d3d_texture, + d3d_shadow, enable_render_surfaces); + } else { + CComPtr<IDirect3DTexture9> d3d_texture = NULL; + HRESULT result = device->CreateTexture(width, height, levels, 0, d3d_format, + D3DPOOL_MANAGED, &d3d_texture, NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new Texture2DD3D9(levels, format, flags, width, height, d3d_texture, + NULL, enable_render_surfaces); + } +} + +// Sets the data in the texture, using LockRect()/UnlockRect(). For dynamic +// textures, it copies the data to the shadow texture, then updates the actual +// texture. For regular texture, it directly modifies the actual texture. +bool Texture2DD3D9::SetData(GAPID3D9 *gapi, + const Volume &volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + DCHECK(d3d_texture_); + IDirect3DTexture9 *lock_texture = d3d_shadow_ ? d3d_shadow_ : d3d_texture_; + + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, 1, level); + TransferInfo src_transfer_info; + MakeTransferInfo(&src_transfer_info, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || level >= levels() || + size < src_transfer_info.total_size) + return false; + + bool full_rect = IsFullVolume(mip_info, volume); + D3DLOCKED_RECT locked_rect; + RECT rect = {volume.x, volume.y, volume.x+volume.width, + volume.y+volume.height}; + DWORD lock_flags = + full_rect && (flags() & texture::kDynamic) ? D3DLOCK_DISCARD : 0; + HR(lock_texture->LockRect(level, &locked_rect, full_rect ? NULL : &rect, + lock_flags)); + + TransferInfo dst_transfer_info; + MakeTransferInfo(&dst_transfer_info, mip_info, volume, locked_rect.Pitch, + slice_pitch); + TransferVolume(volume, mip_info, dst_transfer_info, locked_rect.pBits, + src_transfer_info, data); + + HR(lock_texture->UnlockRect(level)); + if (d3d_shadow_) { + HR(gapi->d3d_device()->UpdateTexture(d3d_shadow_, d3d_texture_)); + } + return true; +} + +// Gets the data from the texture, using LockRect()/UnlockRect(). For dynamic +// textures, it gets the data from the shadow texture, For regular texture, it +// gets it directly from the actual texture. +bool Texture2DD3D9::GetData(GAPID3D9 *gapi, + const Volume &volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + DCHECK(d3d_texture_); + IDirect3DTexture9 *lock_texture = d3d_shadow_ ? d3d_shadow_ : d3d_texture_; + + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, 1, level); + TransferInfo dst_transfer_info; + MakeTransferInfo(&dst_transfer_info, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || level >= levels() || + size < dst_transfer_info.total_size) + return false; + + bool full_rect = IsFullVolume(mip_info, volume); + D3DLOCKED_RECT locked_rect; + RECT rect = {volume.x, volume.y, volume.x+volume.width, + volume.y+volume.height}; + DWORD lock_flags = D3DLOCK_READONLY; + HR(lock_texture->LockRect(level, &locked_rect, full_rect ? NULL : &rect, + lock_flags)); + TransferInfo src_transfer_info; + MakeTransferInfo(&src_transfer_info, mip_info, volume, locked_rect.Pitch, + slice_pitch); + TransferVolume(volume, mip_info, dst_transfer_info, data, + src_transfer_info, locked_rect.pBits); + HR(lock_texture->UnlockRect(level)); + return true; +} + +bool Texture2DD3D9::CreateRenderSurface(int width, + int height, + int mip_level, + int side, + IDirect3DSurface9** direct3d_surface) { + IDirect3DTexture9* d3d_texture = + static_cast<IDirect3DTexture9*>(d3d_base_texture()); + D3DSURFACE_DESC surface_desc; + d3d_texture->GetLevelDesc(mip_level, &surface_desc); + if (width != surface_desc.Width || height != surface_desc.Height) { + return false; + } + HR(d3d_texture->GetSurfaceLevel(mip_level, direct3d_surface)); + return true; +} + +// Texture 3D functions + +// Destroys the 3D texture. +Texture3DD3D9::~Texture3DD3D9() { + DCHECK(d3d_texture_); + d3d_texture_ = NULL; + if (d3d_shadow_) { + d3d_shadow_->Release(); + d3d_shadow_ = NULL; + } +} + +// Creates a 3D texture. For dynamic textures, create it in the default pool, +// and a shadow version in the system memory pool (that we can lock). For +// regular texture, simply create one in the managed pool. +Texture3DD3D9 *Texture3DD3D9::Create(GAPID3D9 *gapi, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + DCHECK_GT(width, 0U); + DCHECK_GT(height, 0U); + DCHECK_GT(depth, 0U); + DCHECK_GT(levels, 0U); + D3DFORMAT d3d_format = D3DFormat(format); + IDirect3DDevice9 *device = gapi->d3d_device(); + if (enable_render_surfaces) { + CComPtr<IDirect3DVolumeTexture9> d3d_texture = NULL; + HRESULT result = device->CreateVolumeTexture(width, height, depth, levels, + D3DUSAGE_RENDERTARGET, + d3d_format, D3DPOOL_DEFAULT, + &d3d_texture, NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new Texture3DD3D9(levels, format, flags, width, height, depth, + d3d_texture, NULL, enable_render_surfaces); + } else if (flags & texture::kDynamic) { + CComPtr<IDirect3DVolumeTexture9> d3d_texture = NULL; + HRESULT result = device->CreateVolumeTexture(width, height, depth, levels, + D3DUSAGE_DYNAMIC, d3d_format, + D3DPOOL_DEFAULT, &d3d_texture, + NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + CComPtr<IDirect3DVolumeTexture9> d3d_shadow = NULL; + result = device->CreateVolumeTexture(width, height, depth, levels, + D3DUSAGE_DYNAMIC, d3d_format, + D3DPOOL_SYSTEMMEM, &d3d_shadow, NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new Texture3DD3D9(levels, format, flags, width, height, depth, + d3d_texture, d3d_shadow, enable_render_surfaces); + } else { + CComPtr<IDirect3DVolumeTexture9> d3d_texture = NULL; + HRESULT result = device->CreateVolumeTexture(width, height, depth, levels, + D3DUSAGE_DYNAMIC, d3d_format, + D3DPOOL_MANAGED, &d3d_texture, + NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new Texture3DD3D9(levels, format, flags, width, height, depth, + d3d_texture, NULL, enable_render_surfaces); + } +} + +// Sets the data in the texture, using LockBox()/UnlockBox(). For dynamic +// textures, it copies the data to the shadow texture, then updates the actual +// texture. For regular texture, it directly modifies the actual texture. +bool Texture3DD3D9::SetData(GAPID3D9 *gapi, + const Volume &volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + DCHECK(d3d_texture_); + IDirect3DVolumeTexture9 *lock_texture = + d3d_shadow_ ? d3d_shadow_ : d3d_texture_; + + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, depth_, level); + TransferInfo src_transfer_info; + MakeTransferInfo(&src_transfer_info, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || level >= levels() || + size < src_transfer_info.total_size) + return false; + + bool full_box = IsFullVolume(mip_info, volume); + D3DLOCKED_BOX locked_box; + D3DBOX box = {volume.x, volume.y, volume.z, volume.x+volume.width, + volume.y+volume.height, volume.z+volume.depth}; + DWORD lock_flags = + full_box && (flags() & texture::kDynamic) ? D3DLOCK_DISCARD : 0; + HR(lock_texture->LockBox(level, &locked_box, full_box ? NULL : &box, + lock_flags)); + + TransferInfo dst_transfer_info; + MakeTransferInfo(&dst_transfer_info, mip_info, volume, locked_box.RowPitch, + locked_box.SlicePitch); + TransferVolume(volume, mip_info, dst_transfer_info, locked_box.pBits, + src_transfer_info, data); + + HR(lock_texture->UnlockBox(level)); + if (d3d_shadow_) { + HR(gapi->d3d_device()->UpdateTexture(d3d_shadow_, d3d_texture_)); + } + return true; +} + +// Gets the data from the texture, using LockBox()/UnlockBox(). For dynamic +// textures, it gets the data from the shadow texture, For regular texture, it +// gets it directly from the actual texture. +bool Texture3DD3D9::GetData(GAPID3D9 *gapi, + const Volume &volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + DCHECK(d3d_texture_); + IDirect3DVolumeTexture9 *lock_texture = + d3d_shadow_ ? d3d_shadow_ : d3d_texture_; + + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, depth_, level); + TransferInfo dst_transfer_info; + MakeTransferInfo(&dst_transfer_info, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || level >= levels() || + size < dst_transfer_info.total_size) + return false; + + bool full_box = IsFullVolume(mip_info, volume); + D3DLOCKED_BOX locked_box; + D3DBOX box = {volume.x, volume.y, volume.z, volume.x+volume.width, + volume.y+volume.height, volume.z+volume.depth}; + DWORD lock_flags = D3DLOCK_READONLY; + HR(lock_texture->LockBox(level, &locked_box, full_box ? NULL : &box, + lock_flags)); + TransferInfo src_transfer_info; + MakeTransferInfo(&src_transfer_info, mip_info, volume, locked_box.RowPitch, + locked_box.SlicePitch); + TransferVolume(volume, mip_info, dst_transfer_info, data, + src_transfer_info, locked_box.pBits); + HR(lock_texture->UnlockBox(level)); + return true; +} + +bool Texture3DD3D9::CreateRenderSurface(int width, + int height, + int mip_level, + int side, + IDirect3DSurface9** direct3d_surface) { + // TODO(rlp): Currently unsupported. + DCHECK(false); + return false; +} + +// Texture Cube functions. + +// Destroys the cube map texture, releasing the D3D texture, and its shadow if +// any. +TextureCubeD3D9::~TextureCubeD3D9() { + DCHECK(d3d_texture_); + d3d_texture_ = NULL; + if (d3d_shadow_) { + d3d_shadow_->Release(); + d3d_shadow_ = NULL; + } +} + +// Creates a cube map texture. For dynamic textures, create it in the default +// pool, and a shadow version in the system memory pool (that we can lock). For +// regular texture, simply create one in the managed pool. +TextureCubeD3D9 *TextureCubeD3D9::Create(GAPID3D9 *gapi, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + DCHECK_GT(side, 0U); + DCHECK_GT(levels, 0U); + D3DFORMAT d3d_format = D3DFormat(format); + IDirect3DDevice9 *device = gapi->d3d_device(); + if (enable_render_surfaces) { + CComPtr<IDirect3DCubeTexture9> d3d_texture = NULL; + HRESULT result = device->CreateCubeTexture(side, levels, + D3DUSAGE_RENDERTARGET, + d3d_format, D3DPOOL_DEFAULT, + &d3d_texture, NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new TextureCubeD3D9(levels, format, flags, side, d3d_texture, NULL, + enable_render_surfaces); + } else if (flags & texture::kDynamic) { + CComPtr<IDirect3DCubeTexture9> d3d_texture = NULL; + HRESULT result = device->CreateCubeTexture(side, levels, D3DUSAGE_DYNAMIC, + d3d_format, D3DPOOL_DEFAULT, + &d3d_texture, NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + CComPtr<IDirect3DCubeTexture9> d3d_shadow = NULL; + result = device->CreateCubeTexture(side, levels, D3DUSAGE_DYNAMIC, + d3d_format, D3DPOOL_SYSTEMMEM, + &d3d_shadow, NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new TextureCubeD3D9(levels, format, flags, side, d3d_texture, + d3d_shadow, enable_render_surfaces); + } else { + CComPtr<IDirect3DCubeTexture9> d3d_texture = NULL; + HRESULT result = device->CreateCubeTexture(side, levels, 0, d3d_format, + D3DPOOL_MANAGED, &d3d_texture, + NULL); + if (result != D3D_OK) { + LOG(ERROR) << "DirectX error when calling CreateTexture: " + << DXGetErrorStringA(result); + return NULL; + } + return new TextureCubeD3D9(levels, format, flags, side, d3d_texture, NULL, + enable_render_surfaces); + } +} + +// Sets the data in the texture, using LockRect()/UnlockRect(). For dynamic +// textures, it copies the data to the shadow texture, then updates the actual +// texture. For regular texture, it directly modifies the actual texture. +bool TextureCubeD3D9::SetData(GAPID3D9 *gapi, + const Volume &volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + DCHECK(d3d_texture_); + IDirect3DCubeTexture9 *lock_texture = + d3d_shadow_ ? d3d_shadow_ : d3d_texture_; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), side_, side_, 1, level); + TransferInfo src_transfer_info; + MakeTransferInfo(&src_transfer_info, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || level >= levels() || + size < src_transfer_info.total_size) + return false; + + D3DCUBEMAP_FACES d3d_face = D3DFace(face); + bool full_rect = IsFullVolume(mip_info, volume); + D3DLOCKED_RECT locked_rect; + RECT rect = {volume.x, volume.y, volume.x+volume.width, + volume.y+volume.height}; + DWORD lock_flags = + full_rect && (flags() & texture::kDynamic) ? D3DLOCK_DISCARD : 0; + HR(lock_texture->LockRect(d3d_face, level, &locked_rect, + full_rect ? NULL : &rect, lock_flags)); + + TransferInfo dst_transfer_info; + MakeTransferInfo(&dst_transfer_info, mip_info, volume, locked_rect.Pitch, + slice_pitch); + TransferVolume(volume, mip_info, dst_transfer_info, locked_rect.pBits, + src_transfer_info, data); + + HR(lock_texture->UnlockRect(d3d_face, level)); + if (d3d_shadow_) { + HR(gapi->d3d_device()->UpdateTexture(d3d_shadow_, d3d_texture_)); + } + return true; +} + +// Gets the data from the texture, using LockRect()/UnlockRect(). For dynamic +// textures, it gets the data from the shadow texture, For regular texture, it +// gets it directly from the actual texture. +bool TextureCubeD3D9::GetData(GAPID3D9 *gapi, + const Volume &volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + DCHECK(d3d_texture_); + IDirect3DCubeTexture9 *lock_texture = + d3d_shadow_ ? d3d_shadow_ : d3d_texture_; + + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), side_, side_, 1, level); + TransferInfo dst_transfer_info; + MakeTransferInfo(&dst_transfer_info, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || level >= levels() || + size < dst_transfer_info.total_size) + return false; + + D3DCUBEMAP_FACES d3d_face = D3DFace(face); + bool full_rect = IsFullVolume(mip_info, volume); + D3DLOCKED_RECT locked_rect; + RECT rect = {volume.x, volume.y, volume.x+volume.width, + volume.y+volume.height}; + DWORD lock_flags = D3DLOCK_READONLY; + HR(lock_texture->LockRect(d3d_face, level, &locked_rect, + full_rect ? NULL : &rect, lock_flags)); + TransferInfo src_transfer_info; + MakeTransferInfo(&src_transfer_info, mip_info, volume, locked_rect.Pitch, + slice_pitch); + TransferVolume(volume, mip_info, dst_transfer_info, data, + src_transfer_info, locked_rect.pBits); + HR(lock_texture->UnlockRect(d3d_face, level)); + return true; +} + +bool TextureCubeD3D9::CreateRenderSurface( + int width, + int height, + int mip_level, + int side, + IDirect3DSurface9** direct3d_surface) { + IDirect3DCubeTexture9* d3d_cube_texture = + static_cast<IDirect3DCubeTexture9*>(d3d_base_texture()); + D3DSURFACE_DESC surface_desc; + d3d_cube_texture->GetLevelDesc(mip_level, &surface_desc); + if (width != surface_desc.Width || height != surface_desc.Height || + side < 0 || side > 5) { + return false; + } + HR(d3d_cube_texture->GetCubeMapSurface( + D3DFace(static_cast<texture::Face>(side)), + mip_level, + direct3d_surface)); + return true; +} + +// GAPID3D9 functions. + +// Destroys a texture resource. +parse_error::ParseError GAPID3D9::DestroyTexture(ResourceId id) { + // Dirty effect, because this texture id may be used + DirtyEffect(); + return textures_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Creates a 2D texture resource. +parse_error::ParseError GAPID3D9::CreateTexture2D( + ResourceId id, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + Texture2DD3D9 *texture = Texture2DD3D9::Create(this, width, height, levels, + format, flags, + enable_render_surfaces); + if (!texture) return parse_error::kParseInvalidArguments; + // Dirty effect, because this texture id may be used + DirtyEffect(); + textures_.Assign(id, texture); + return parse_error::kParseNoError; +} + +// Creates a 3D texture resource. +parse_error::ParseError GAPID3D9::CreateTexture3D( + ResourceId id, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + Texture3DD3D9 *texture = Texture3DD3D9::Create(this, width, height, depth, + levels, format, flags, + enable_render_surfaces); + if (!texture) return parse_error::kParseInvalidArguments; + // Dirty effect, because this texture id may be used + DirtyEffect(); + textures_.Assign(id, texture); + return parse_error::kParseNoError; +} + +// Creates a cube map texture resource. +parse_error::ParseError GAPID3D9::CreateTextureCube( + ResourceId id, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + TextureCubeD3D9 *texture = TextureCubeD3D9::Create(this, side, levels, + format, flags, + enable_render_surfaces); + if (!texture) return parse_error::kParseInvalidArguments; + // Dirty effect, because this texture id may be used + DirtyEffect(); + textures_.Assign(id, texture); + return parse_error::kParseNoError; +} + +// Copies the data into a texture resource. +parse_error::ParseError GAPID3D9::SetTextureData( + ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + TextureD3D9 *texture = textures_.Get(id); + if (!texture) + return parse_error::kParseInvalidArguments; + Volume volume = {x, y, z, width, height, depth}; + return texture->SetData(this, volume, level, face, row_pitch, slice_pitch, + size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Copies the data from a texture resource. +parse_error::ParseError GAPID3D9::GetTextureData( + ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + TextureD3D9 *texture = textures_.Get(id); + if (!texture) + return parse_error::kParseInvalidArguments; + Volume volume = {x, y, z, width, height, depth}; + return texture->GetData(this, volume, level, face, row_pitch, slice_pitch, + size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/texture_d3d9.h b/o3d/gpu/command_buffer/service/texture_d3d9.h new file mode 100644 index 0000000..de913a8 --- /dev/null +++ b/o3d/gpu/command_buffer/service/texture_d3d9.h @@ -0,0 +1,282 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef GPU_COMMAND_BUFFER_SERVICE_TEXTURE_D3D9_H_ +#define GPU_COMMAND_BUFFER_SERVICE_TEXTURE_D3D9_H_ + +// This file contains the definition of the D3D9 versions of texture-related +// resource classes. +#include <atlbase.h> + +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/d3d9_utils.h" +#include "gpu/command_buffer/service/resource.h" +#include "gpu/command_buffer/service/texture_utils.h" + +namespace command_buffer { +namespace o3d { + +class GAPID3D9; + +// The base class for a D3D texture resource, providing access to the base D3D +// texture that can be assigned to an effect parameter or a sampler unit. +class TextureD3D9 : public Texture { + public: + TextureD3D9(texture::Type type, + unsigned int levels, + texture::Format format, + bool enable_render_surfaces, + unsigned int flags) + : Texture(type, levels, format, enable_render_surfaces, flags) {} + // Gets the D3D base texture. + virtual IDirect3DBaseTexture9 *d3d_base_texture() const = 0; + // Sets data into a texture resource. + virtual bool SetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) = 0; + // Gets data from a texture resource. + virtual bool GetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) = 0; + // Creates the render surface, returning false if unable to. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side, + IDirect3DSurface9** direct3d_surface) = 0; + static D3DFORMAT D3DFormat(texture::Format format); + static D3DCUBEMAP_FACES D3DFace(texture::Face face); + private: + DISALLOW_COPY_AND_ASSIGN(TextureD3D9); +}; + +// A 2D texture resource for D3D. +class Texture2DD3D9 : public TextureD3D9 { + public: + Texture2DD3D9(unsigned int levels, + texture::Format format, + unsigned int flags, + unsigned int width, + unsigned int height, + IDirect3DTexture9 *texture, + IDirect3DTexture9 *shadow, + bool enable_render_surfaces) + : TextureD3D9(texture::kTexture2d, levels, format, + enable_render_surfaces, flags), + width_(width), + height_(height), + d3d_texture_(texture), + d3d_shadow_(shadow) {} + virtual ~Texture2DD3D9(); + + // Creates a 2D texture resource. + static Texture2DD3D9 *Create(GAPID3D9 *gapi, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + // Sets data into a 2D texture resource. + virtual bool SetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + // Gets data from a 2D texture resource. + virtual bool GetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + // Create a render surface which matches this texture type. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side, + IDirect3DSurface9** direct3d_surface); + // Gets the D3D base texture. + virtual IDirect3DBaseTexture9 *d3d_base_texture() const { + return d3d_texture_; + } + private: + unsigned int width_; + unsigned int height_; + CComPtr<IDirect3DTexture9> d3d_texture_; + IDirect3DTexture9 *d3d_shadow_; + DISALLOW_COPY_AND_ASSIGN(Texture2DD3D9); +}; + +// A 3D texture resource for D3D. +class Texture3DD3D9 : public TextureD3D9 { + public: + Texture3DD3D9(unsigned int levels, + texture::Format format, + unsigned int flags, + unsigned int width, + unsigned int height, + unsigned int depth, + IDirect3DVolumeTexture9 *texture, + IDirect3DVolumeTexture9 *shadow, + bool enable_render_surfaces) + : TextureD3D9(texture::kTexture3d, levels, format, + enable_render_surfaces, flags), + width_(width), + height_(height), + depth_(depth), + d3d_texture_(texture), + d3d_shadow_(shadow) {} + virtual ~Texture3DD3D9(); + // Creates a 3D texture resource. + static Texture3DD3D9 *Create(GAPID3D9 *gapi, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + // Sets data into a 3D texture resource. + virtual bool SetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + // Gets data from a 3D texture resource. + virtual bool GetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + // Create a render surface which matches this texture type. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side, + IDirect3DSurface9** direct3d_surface); + // Gets the D3D base texture. + virtual IDirect3DBaseTexture9 *d3d_base_texture() const { + return d3d_texture_; + } + private: + unsigned int width_; + unsigned int height_; + unsigned int depth_; + CComPtr<IDirect3DVolumeTexture9> d3d_texture_; + IDirect3DVolumeTexture9 *d3d_shadow_; + DISALLOW_COPY_AND_ASSIGN(Texture3DD3D9); +}; + +// A cube map texture resource for D3D. +class TextureCubeD3D9 : public TextureD3D9 { + public: + TextureCubeD3D9(unsigned int levels, + texture::Format format, + unsigned int flags, + unsigned int side, + IDirect3DCubeTexture9 *texture, + IDirect3DCubeTexture9 *shadow, + bool enable_render_surfaces) + : TextureD3D9(texture::kTextureCube, levels, format, + enable_render_surfaces, flags), + side_(side), + d3d_texture_(texture), + d3d_shadow_(shadow) {} + virtual ~TextureCubeD3D9(); + // Creates a cube map texture resource. + static TextureCubeD3D9 *Create(GAPID3D9 *gapi, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + // Sets data into a cube map texture resource. + virtual bool SetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + // Gets data from a cube map texture resource. + virtual bool GetData(GAPID3D9 *gapi, + const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + // Create a render surface which matches this texture type. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side, + IDirect3DSurface9** direct3d_surface); + // Gets the D3D base texture. + virtual IDirect3DBaseTexture9 *d3d_base_texture() const { + return d3d_texture_; + } + private: + unsigned int side_; + CComPtr<IDirect3DCubeTexture9> d3d_texture_; + IDirect3DCubeTexture9 *d3d_shadow_; + DISALLOW_COPY_AND_ASSIGN(TextureCubeD3D9); +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_TEXTURE_D3D9_H_ diff --git a/o3d/gpu/command_buffer/service/texture_gl.cc b/o3d/gpu/command_buffer/service/texture_gl.cc new file mode 100644 index 0000000..386c5ec --- /dev/null +++ b/o3d/gpu/command_buffer/service/texture_gl.cc @@ -0,0 +1,767 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file implements the texture-related GAPI functions on GL. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/service/gapi_gl.h" +#include "gpu/command_buffer/service/texture_gl.h" + +namespace command_buffer { +namespace o3d { + +namespace { + +// Gets the GL internal format, format and type corresponding to a command +// buffer texture format. +bool GetGLFormatType(texture::Format format, + GLenum *internal_format, + GLenum *gl_format, + GLenum *gl_type) { + switch (format) { + case texture::kXRGB8: { + *internal_format = GL_RGB; + *gl_format = GL_BGRA; + *gl_type = GL_UNSIGNED_BYTE; + break; + } + case texture::kARGB8: { + *internal_format = GL_RGBA; + *gl_format = GL_BGRA; + *gl_type = GL_UNSIGNED_BYTE; + break; + } + case texture::kABGR16F: { + *internal_format = GL_RGBA16F_ARB; + *gl_format = GL_RGBA; + *gl_type = GL_HALF_FLOAT_ARB; + break; + } + case texture::kR32F: { + *internal_format = GL_LUMINANCE32F_ARB; + *gl_format = GL_LUMINANCE; + *gl_type = GL_FLOAT; + break; + } + case texture::kABGR32F: { + *internal_format = GL_RGBA32F_ARB; + *gl_format = GL_BGRA; + *gl_type = GL_FLOAT; + break; + } + case texture::kDXT1: { + *internal_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + *gl_format = 0; + *gl_type = 0; + break; + } + // TODO(petersont): Add DXT3/5 support. + default: + return false; + } + + return true; +} + +// Helper class used to prepare image data to match the layout that +// glTexImage* and glCompressedTexImage* expect. +class SetImageHelper { + public: + SetImageHelper() + : buffer_(NULL), + image_data_(NULL), + image_size_(0) { + } + + // Initializes the helper with the input data, re-using the input buffer if + // possible, or copying it into a temporary one. + bool Initialize(const MipLevelInfo &mip_info, + const Volume& volume, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int src_size, + const void *data) { + TransferInfo src_transfer_info; + MakeTransferInfo(&src_transfer_info, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || + src_size < src_transfer_info.total_size) + return false; + if (!src_transfer_info.packed) { + TransferInfo dst_transfer_info; + MakePackedTransferInfo(&dst_transfer_info, mip_info, volume); + buffer_.reset(new unsigned char[dst_transfer_info.total_size]); + TransferVolume(volume, mip_info, dst_transfer_info, buffer_.get(), + src_transfer_info, data); + image_data_ = buffer_.get(); + image_size_ = dst_transfer_info.total_size; + } else { + image_data_ = data; + image_size_ = src_transfer_info.total_size; + } + return true; + } + + // Gets the buffer that contains the data in the GL format. + const void *image_data() { return image_data_; } + // Gets the size of the buffer as GL expects it. + unsigned int image_size() { return image_size_; } + + private: + scoped_array<unsigned char> buffer_; + const void *image_data_; + unsigned int image_size_; + DISALLOW_COPY_AND_ASSIGN(SetImageHelper); +}; + +// Helper class used to retrieve image data to match the layout that +// glGetTexImage and glGetCompressedTexImage expect. +class GetImageHelper { + public: + GetImageHelper() + : dst_data_(NULL), + buffer_(NULL), + image_data_(NULL) { + memset(&mip_info_, 0, sizeof(mip_info_)); + memset(&volume_, 0, sizeof(volume_)); + memset(&dst_transfer_info_, 0, sizeof(dst_transfer_info_)); + memset(&src_transfer_info_, 0, sizeof(src_transfer_info_)); + } + + // Initialize the helper to make available a buffer to get the data from GL. + // It will re-use the passed in buffer if the layout matches GL, or allocate + // a temporary one. + bool Initialize(const MipLevelInfo &mip_info, + const Volume& volume, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int dst_size, + void *dst_data) { + mip_info_ = mip_info; + volume_ = volume; + dst_data_ = dst_data; + MakeTransferInfo(&dst_transfer_info_, mip_info, volume, row_pitch, + slice_pitch); + if (!CheckVolume(mip_info, volume) || + dst_size < dst_transfer_info_.total_size) + return false; + + if (!IsFullVolume(mip_info, volume) || !dst_transfer_info_.packed) { + // We can only retrieve the full image from GL. + Volume full_volume = { + 0, 0, 0, + mip_info.width, mip_info.height, mip_info.depth + }; + MakePackedTransferInfo(&src_transfer_info_, mip_info, full_volume); + buffer_.reset(new unsigned char[src_transfer_info_.total_size]); + image_data_ = buffer_.get(); + } else { + image_data_ = dst_data; + } + return true; + } + + // Finalize the helper, copying the data into the final buffer if needed. + void Finalize() { + if (!buffer_.get()) return; + unsigned int offset = + volume_.x / mip_info_.block_size_x * mip_info_.block_bpp + + volume_.y / mip_info_.block_size_y * src_transfer_info_.row_pitch + + volume_.z * src_transfer_info_.slice_pitch; + src_transfer_info_.row_size = dst_transfer_info_.row_size; + TransferVolume(volume_, mip_info_, dst_transfer_info_, dst_data_, + src_transfer_info_, buffer_.get() + offset); + } + + // Gets the buffer that can receive the data from GL. + void *image_data() { return image_data_; } + + private: + MipLevelInfo mip_info_; + Volume volume_; + TransferInfo dst_transfer_info_; + TransferInfo src_transfer_info_; + void *dst_data_; + scoped_array<unsigned char> buffer_; + void *image_data_; + DISALLOW_COPY_AND_ASSIGN(GetImageHelper); +}; + +} // anonymous namespace + +TextureGL::~TextureGL() { + glDeleteTextures(1, &gl_texture_); + CHECK_GL_ERROR(); +} + +Texture2DGL *Texture2DGL::Create(unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + DCHECK_GT(width, 0U); + DCHECK_GT(height, 0U); + DCHECK_GT(levels, 0U); + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format, &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); // Was checked in the decoder. + GLuint gl_texture = 0; + glGenTextures(1, &gl_texture); + glBindTexture(GL_TEXTURE_2D, gl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels - 1); + // glCompressedTexImage2D does't accept NULL as a parameter, so we need + // to pass in some data. + scoped_array<unsigned char> buffer; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, width, height, 1, 0); + unsigned int size = GetMipLevelSize(mip_info); + buffer.reset(new unsigned char[size]); + memset(buffer.get(), 0, size); + + unsigned int mip_width = width; + unsigned int mip_height = height; + + for (unsigned int i = 0; i < levels; ++i) { + if (gl_format) { + glTexImage2D(GL_TEXTURE_2D, i, gl_internal_format, mip_width, mip_height, + 0, gl_format, gl_type, buffer.get()); + } else { + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, width, height, 1, i); + unsigned int size = GetMipLevelSize(mip_info); + glCompressedTexImage2D(GL_TEXTURE_2D, i, gl_internal_format, mip_width, + mip_height, 0, size, buffer.get()); + } + mip_width = std::max(1U, mip_width >> 1); + mip_height = std::max(1U, mip_height >> 1); + } + return new Texture2DGL( + levels, format, enable_render_surfaces, flags, width, height, gl_texture); +} + +// Sets data into a 2D texture resource. +bool Texture2DGL::SetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + if (level >= levels()) + return false; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, 1, level); + SetImageHelper helper; + if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) + return false; + + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); + glBindTexture(GL_TEXTURE_2D, gl_texture_); + if (gl_format) { + glTexSubImage2D(GL_TEXTURE_2D, level, volume.x, volume.y, volume.width, + volume.height, gl_format, gl_type, helper.image_data()); + } else { + glCompressedTexSubImage2D(GL_TEXTURE_2D, level, volume.x, volume.y, + volume.width, volume.height, gl_internal_format, + helper.image_size(), helper.image_data()); + } + return true; +} + +bool Texture2DGL::GetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + if (level >= levels()) + return false; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, 1, level); + GetImageHelper helper; + if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) + return false; + + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); + glBindTexture(GL_TEXTURE_2D, gl_texture_); + if (gl_format) { + glGetTexImage(GL_TEXTURE_2D, level, gl_format, gl_type, + helper.image_data()); + } else { + glGetCompressedTexImage(GL_TEXTURE_2D, level, helper.image_data()); + } + + helper.Finalize(); + return true; +} + +bool Texture2DGL::CreateRenderSurface(int width, + int height, + int mip_level, + int side) { + return false; +} + +bool Texture2DGL::InstallFrameBufferObjects( + RenderSurfaceGL *gl_surface) { + ::glFramebufferTexture2DEXT( + GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, + gl_texture_, + gl_surface->mip_level()); + + GLenum framebuffer_status = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (GL_FRAMEBUFFER_COMPLETE_EXT != framebuffer_status) { + return false; + } + + return true; +} + +Texture3DGL *Texture3DGL::Create(unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + DCHECK_GT(width, 0U); + DCHECK_GT(height, 0U); + DCHECK_GT(depth, 0U); + DCHECK_GT(levels, 0U); + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format, &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); // Was checked in the decoder. + GLuint gl_texture = 0; + glGenTextures(1, &gl_texture); + glBindTexture(GL_TEXTURE_3D, gl_texture); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, levels - 1); + // glCompressedTexImage3D does't accept NULL as a parameter, so we need + // to pass in some data. + scoped_array<unsigned char> buffer; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, width, height, depth, 0); + unsigned int size = GetMipLevelSize(mip_info); + buffer.reset(new unsigned char[size]); + memset(buffer.get(), 0, size); + + unsigned int mip_width = width; + unsigned int mip_height = height; + unsigned int mip_depth = depth; + for (unsigned int i = 0; i < levels; ++i) { + if (gl_format) { + glTexImage3D(GL_TEXTURE_3D, i, gl_internal_format, mip_width, mip_height, + mip_depth, 0, gl_format, gl_type, buffer.get()); + } else { + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, width, height, depth, i); + unsigned int size = GetMipLevelSize(mip_info); + glCompressedTexImage3D(GL_TEXTURE_3D, i, gl_internal_format, mip_width, + mip_height, mip_depth, 0, size, buffer.get()); + } + mip_width = std::max(1U, mip_width >> 1); + mip_height = std::max(1U, mip_height >> 1); + mip_depth = std::max(1U, mip_depth >> 1); + } + return new Texture3DGL(levels, format, enable_render_surfaces, flags, width, + height, depth, gl_texture); +} + +bool Texture3DGL::SetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + if (level >= levels()) + return false; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, depth_, level); + SetImageHelper helper; + if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) + return false; + + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); + glBindTexture(GL_TEXTURE_3D, gl_texture_); + if (gl_format) { + glTexSubImage3D(GL_TEXTURE_3D, level, volume.x, volume.y, volume.z, + volume.width, volume.height, volume.depth, + gl_format, gl_type, helper.image_data()); + } else { + glCompressedTexSubImage3D(GL_TEXTURE_3D, level, volume.x, volume.y, + volume.z, volume.width, volume.height, + volume.depth, gl_internal_format, + helper.image_size(), helper.image_data()); + } + return true; +} + +bool Texture3DGL::GetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + if (level >= levels()) + return false; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), width_, height_, depth_, level); + GetImageHelper helper; + if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) + return false; + + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); + glBindTexture(GL_TEXTURE_3D, gl_texture_); + if (gl_format) { + glGetTexImage(GL_TEXTURE_3D, level, gl_format, gl_type, + helper.image_data()); + } else { + glGetCompressedTexImage(GL_TEXTURE_3D, level, helper.image_data()); + } + + helper.Finalize(); + return true; +} + +bool Texture3DGL::CreateRenderSurface(int width, + int height, + int mip_level, + int side) { + return false; +} + +bool Texture3DGL::InstallFrameBufferObjects( + RenderSurfaceGL *gl_surface) { + return false; +} + +TextureCubeGL *TextureCubeGL::Create(unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + DCHECK_GT(side, 0U); + DCHECK_GT(levels, 0U); + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format, &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); // Was checked in the decoder. + GLuint gl_texture = 0; + glGenTextures(1, &gl_texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, + levels-1); + // glCompressedTexImage2D does't accept NULL as a parameter, so we need + // to pass in some data. + scoped_array<unsigned char> buffer; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, side, side, 1, 0); + unsigned int size = GetMipLevelSize(mip_info); + buffer.reset(new unsigned char[size]); + memset(buffer.get(), 0, size); + + unsigned int mip_side = side; + for (unsigned int i = 0; i < levels; ++i) { + if (gl_format) { + for (unsigned int face = 0; face < 6; ++face) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, + gl_internal_format, mip_side, mip_side, + 0, gl_format, gl_type, buffer.get()); + } + } else { + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, side, side, 1, i); + unsigned int size = GetMipLevelSize(mip_info); + for (unsigned int face = 0; face < 6; ++face) { + glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, + gl_internal_format, mip_side, mip_side, 0, size, + buffer.get()); + } + } + mip_side = std::max(1U, mip_side >> 1); + } + return new TextureCubeGL( + levels, format, enable_render_surfaces, flags, side, gl_texture); +} + +// Check that GL_TEXTURE_CUBE_MAP_POSITIVE_X + face yields the correct GLenum. +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFacePositiveX == + GL_TEXTURE_CUBE_MAP_POSITIVE_X, POSITIVE_X_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFaceNegativeX == + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, NEGATIVE_X_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFacePositiveY == + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, POSITIVE_Y_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFaceNegativeY == + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, NEGATIVE_Y_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFacePositiveZ == + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, POSITIVE_Z_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::kFaceNegativeZ == + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, NEGATIVE_Z_ENUMS_DO_NOT_MATCH); + +bool TextureCubeGL::SetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + if (level >= levels()) + return false; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), side_, side_, 1, level); + SetImageHelper helper; + if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) + return false; + + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); + glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture_); + GLenum face_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; + if (gl_format) { + glTexSubImage2D(face_target, level, volume.x, volume.y, volume.width, + volume.height, gl_format, gl_type, helper.image_data()); + } else { + glCompressedTexSubImage2D(face_target, level, volume.x, volume.y, + volume.width, volume.height, gl_internal_format, + helper.image_size(), helper.image_data()); + } + return true; +} + +bool TextureCubeGL::GetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + if (level >= levels()) + return false; + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format(), side_, side_, 1, level); + GetImageHelper helper; + if (!helper.Initialize(mip_info, volume, row_pitch, slice_pitch, size, data)) + return false; + + GLenum gl_internal_format = 0; + GLenum gl_format = 0; + GLenum gl_type = 0; + bool r = GetGLFormatType(format(), &gl_internal_format, &gl_format, &gl_type); + DCHECK(r); + glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture_); + GLenum face_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; + if (gl_format) { + glGetTexImage(face_target, level, gl_format, gl_type, + helper.image_data()); + } else { + glGetCompressedTexImage(face_target, level, helper.image_data()); + } + + helper.Finalize(); + return true; +} + +bool TextureCubeGL::CreateRenderSurface(int width, + int height, + int mip_level, + int side) { + return false; +} + +bool TextureCubeGL::InstallFrameBufferObjects( + RenderSurfaceGL *gl_surface) { + ::glFramebufferTexture2DEXT( + GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + gl_surface->side(), + gl_texture_, + gl_surface->mip_level()); + + GLenum framebuffer_status = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (GL_FRAMEBUFFER_COMPLETE_EXT != framebuffer_status) { + return false; + } + + return true; +} + + +// Destroys a texture resource. +parse_error::ParseError GAPIGL::DestroyTexture(ResourceId id) { + // Dirty effect, because this texture id may be used. + DirtyEffect(); + return textures_.Destroy(id) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Creates a 2D texture resource. +parse_error::ParseError GAPIGL::CreateTexture2D( + ResourceId id, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + Texture2DGL *texture = Texture2DGL::Create( + width, height, levels, format, flags, enable_render_surfaces); + if (!texture) return parse_error::kParseInvalidArguments; + // Dirty effect, because this texture id may be used. + DirtyEffect(); + textures_.Assign(id, texture); + return parse_error::kParseNoError; +} + +// Creates a 3D texture resource. +parse_error::ParseError GAPIGL::CreateTexture3D( + ResourceId id, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + Texture3DGL *texture = Texture3DGL::Create( + width, height, depth, levels, format, flags, enable_render_surfaces); + if (!texture) return parse_error::kParseInvalidArguments; + // Dirty effect, because this texture id may be used. + DirtyEffect(); + textures_.Assign(id, texture); + return parse_error::kParseNoError; +} + +// Creates a cube map texture resource. +parse_error::ParseError GAPIGL::CreateTextureCube( + ResourceId id, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces) { + TextureCubeGL *texture = TextureCubeGL::Create( + side, levels, format, flags, enable_render_surfaces); + if (!texture) return parse_error::kParseInvalidArguments; + // Dirty effect, because this texture id may be used. + DirtyEffect(); + textures_.Assign(id, texture); + return parse_error::kParseNoError; +} + +// Copies the data into a texture resource. +parse_error::ParseError GAPIGL::SetTextureData( + ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) { + TextureGL *texture = textures_.Get(id); + if (!texture) + return parse_error::kParseInvalidArguments; + Volume volume = {x, y, z, width, height, depth}; + // Dirty effect: SetData may need to call glBindTexture which will mess up the + // sampler parameters. + DirtyEffect(); + return texture->SetData(volume, level, face, row_pitch, slice_pitch, + size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +// Copies the data from a texture resource. +parse_error::ParseError GAPIGL::GetTextureData( + ResourceId id, + unsigned int x, + unsigned int y, + unsigned int z, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) { + TextureGL *texture = textures_.Get(id); + if (!texture) + return parse_error::kParseInvalidArguments; + Volume volume = {x, y, z, width, height, depth}; + // Dirty effect: GetData may need to call glBindTexture which will mess up the + // sampler parameters. + DirtyEffect(); + return texture->GetData(volume, level, face, row_pitch, slice_pitch, + size, data) ? + parse_error::kParseNoError : + parse_error::kParseInvalidArguments; +} + +} // namespace o3d +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/texture_gl.h b/o3d/gpu/command_buffer/service/texture_gl.h new file mode 100644 index 0000000..d3e774c --- /dev/null +++ b/o3d/gpu/command_buffer/service/texture_gl.h @@ -0,0 +1,284 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file declares the TextureGL, Texture2DGL, Texture3DGL and TextureCubeGL +// classes. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_TEXTURE_GL_H_ +#define GPU_COMMAND_BUFFER_SERVICE_TEXTURE_GL_H_ + +#include "gpu/command_buffer/common/gapi_interface.h" +#include "gpu/command_buffer/service/gl_utils.h" +#include "gpu/command_buffer/service/resource.h" +#include "gpu/command_buffer/service/texture_utils.h" + +namespace command_buffer { +namespace o3d { + +class RenderDepthStencilSurfaceGL; +class RenderSurfaceGL; + +// The base class for a GL texture resource, providing access to the base GL +// texture that can be assigned to an effect parameter or a sampler unit. +class TextureGL : public Texture { + public: + TextureGL(texture::Type type, + unsigned int levels, + texture::Format format, + bool enable_render_surfaces, + unsigned int flags, + GLuint gl_texture) + : Texture(type, levels, format, enable_render_surfaces, flags), + gl_texture_(gl_texture) {} + virtual ~TextureGL(); + + // Gets the GL texture object. + GLuint gl_texture() const { return gl_texture_; } + + // Sets data into a texture resource. + virtual bool SetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data) = 0; + + // Gets data from a texture resource. + virtual bool GetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data) = 0; + + // Creates the render surface, returning false if unable to. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side) = 0; + + virtual bool InstallFrameBufferObjects( + RenderSurfaceGL *gl_surface) = 0; + + protected: + const GLuint gl_texture_; + + private: + DISALLOW_COPY_AND_ASSIGN(TextureGL); +}; + +// A 2D texture resource for GL. +class Texture2DGL : public TextureGL { + public: + Texture2DGL(unsigned int levels, + texture::Format format, + bool enable_render_surfaces, + unsigned int flags, + unsigned int width, + unsigned int height, + GLuint gl_texture) + : TextureGL(texture::kTexture2d, + levels, + format, + enable_render_surfaces, + flags, + gl_texture), + width_(width), + height_(height) {} + + // Creates a 2D texture resource. + static Texture2DGL *Create(unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Sets data into a 2D texture resource. + virtual bool SetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + + // Gets data from a 2D texture resource. + virtual bool GetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + + // Create a render surface which matches this texture type. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side); + + virtual bool InstallFrameBufferObjects( + RenderSurfaceGL *gl_surface); + + private: + unsigned int width_; + unsigned int height_; + DISALLOW_COPY_AND_ASSIGN(Texture2DGL); +}; + +// A 3D texture resource for GL. +class Texture3DGL : public TextureGL { + public: + Texture3DGL(unsigned int levels, + texture::Format format, + bool enable_render_surfaces, + unsigned int flags, + unsigned int width, + unsigned int height, + unsigned int depth, + GLuint gl_texture) + : TextureGL(texture::kTexture3d, + levels, + format, + enable_render_surfaces, + flags, + gl_texture), + width_(width), + height_(height), + depth_(depth) {} + + // Creates a 3D texture resource. + static Texture3DGL *Create(unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Sets data into a 3D texture resource. + virtual bool SetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + + // Gets data from a 3D texture resource. + virtual bool GetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + + // Create a render surface which matches this texture type. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side); + + virtual bool InstallFrameBufferObjects( + RenderSurfaceGL *gl_surface); + + private: + unsigned int width_; + unsigned int height_; + unsigned int depth_; + DISALLOW_COPY_AND_ASSIGN(Texture3DGL); +}; + +// A cube map texture resource for GL. +class TextureCubeGL : public TextureGL { + public: + TextureCubeGL(unsigned int levels, + texture::Format format, + bool render_surface_enabled, + unsigned int flags, + unsigned int side, + GLuint gl_texture) + : TextureGL(texture::kTextureCube, + levels, + format, + render_surface_enabled, + flags, + gl_texture), + side_(side) {} + + // Creates a cube map texture resource. + static TextureCubeGL *Create(unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags, + bool enable_render_surfaces); + + // Sets data into a cube map texture resource. + virtual bool SetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + const void *data); + + // Gets data from a cube map texture resource. + virtual bool GetData(const Volume& volume, + unsigned int level, + texture::Face face, + unsigned int row_pitch, + unsigned int slice_pitch, + unsigned int size, + void *data); + + // Create a render surface which matches this texture type. + virtual bool CreateRenderSurface(int width, + int height, + int mip_level, + int side); + + virtual bool InstallFrameBufferObjects( + RenderSurfaceGL *gl_surface); + + private: + unsigned int side_; + DISALLOW_COPY_AND_ASSIGN(TextureCubeGL); +}; + +} // namespace o3d +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_TEXTURE_GL_H_ diff --git a/o3d/gpu/command_buffer/service/texture_utils.cc b/o3d/gpu/command_buffer/service/texture_utils.cc new file mode 100644 index 0000000..4aac8fa --- /dev/null +++ b/o3d/gpu/command_buffer/service/texture_utils.cc @@ -0,0 +1,105 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of some utilities for textures. + +#include "gpu/command_buffer/service/precompile.h" + +#include <stdlib.h> + +#include "gpu/command_buffer/service/texture_utils.h" + +namespace command_buffer { + +void MakeTransferInfo(TransferInfo *transfer_info, + const MipLevelInfo &mip_level, + const Volume &volume, + unsigned int row_pitch, + unsigned int slice_pitch) { + transfer_info->row_pitch = row_pitch; + transfer_info->slice_pitch = slice_pitch; + transfer_info->row_size = + volume.width / mip_level.block_size_x * mip_level.block_bpp; + transfer_info->slice_size = transfer_info->row_size + + (volume.height / mip_level.block_size_y - 1) * row_pitch; + transfer_info->total_size = transfer_info->slice_size + + (volume.depth - 1) * slice_pitch; + transfer_info->packed = (transfer_info->row_size == row_pitch) && + (volume.depth == 1 || transfer_info->slice_size == slice_pitch); +} + +void MakePackedTransferInfo(TransferInfo *transfer_info, + const MipLevelInfo &mip_level, + const Volume &volume) { + transfer_info->row_size = + volume.width / mip_level.block_size_x * mip_level.block_bpp; + transfer_info->row_pitch = transfer_info->row_size; + transfer_info->slice_size = + volume.height / mip_level.block_size_y * transfer_info->row_pitch; + transfer_info->slice_pitch = transfer_info->slice_size; + transfer_info->total_size = volume.depth * transfer_info->slice_pitch; + transfer_info->packed = true; +} + +// Transfers a volume of texels. +void TransferVolume(const Volume &volume, + const MipLevelInfo &mip_level, + const TransferInfo &dst_transfer_info, + void *dst_data, + const TransferInfo &src_transfer_info, + const void *src_data) { + DCHECK_EQ(src_transfer_info.row_size, dst_transfer_info.row_size); + if (src_transfer_info.packed && dst_transfer_info.packed) { + // fast path + DCHECK_EQ(src_transfer_info.total_size, dst_transfer_info.total_size); + DCHECK_EQ(src_transfer_info.row_pitch, dst_transfer_info.row_pitch); + DCHECK_EQ(src_transfer_info.slice_pitch, dst_transfer_info.slice_pitch); + memcpy(dst_data, src_data, src_transfer_info.total_size); + } else { + const char *src = static_cast<const char *>(src_data); + char *dst = static_cast<char *>(dst_data); + for (unsigned int slice = 0; slice < volume.depth; ++slice) { + const char *row_src = src; + char *row_dst = dst; + for (unsigned int row = 0; row < volume.height; + row += mip_level.block_size_y) { + memcpy(row_dst, row_src, src_transfer_info.row_size); + row_src += src_transfer_info.row_pitch; + row_dst += dst_transfer_info.row_pitch; + } + src += src_transfer_info.slice_pitch; + dst += dst_transfer_info.slice_pitch; + } + } +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/texture_utils.h b/o3d/gpu/command_buffer/service/texture_utils.h new file mode 100644 index 0000000..0ace181 --- /dev/null +++ b/o3d/gpu/command_buffer/service/texture_utils.h @@ -0,0 +1,156 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file declares some utilities for textures, in particular to deal with +// in-memory texture data and layout. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_TEXTURE_UTILS_H_ +#define GPU_COMMAND_BUFFER_SERVICE_CROSS_TEXTURE_UTILS_H_ + +#include "gpu/command_buffer/common/logging.h" +#include "gpu/command_buffer/common/resource.h" + +namespace command_buffer { + +// Structure describing a volume of pixels. +struct Volume { + unsigned int x; + unsigned int y; + unsigned int z; + unsigned int width; + unsigned int height; + unsigned int depth; +}; + +// Structure describing the dimensions and structure of a mip level. +struct MipLevelInfo { + unsigned int block_bpp; + unsigned int block_size_x; + unsigned int block_size_y; + unsigned int width; + unsigned int height; + unsigned int depth; +}; + +// Structure describing a memory layout for transfers. +struct TransferInfo { + unsigned int row_size; // size in bytes of a row of blocks. + unsigned int row_pitch; // number of bytes between 2 successive rows. + unsigned int slice_size; // size in bytes of a slice of data. + unsigned int slice_pitch; // number of bytes between 2 successive slices. + unsigned int total_size; // total size of the data. + bool packed; // indicates whether the data is tightly packed. +}; + +// Round a value up, so that it is divisible by the block size. +static inline unsigned int RoundToBlockSize(unsigned int base, + unsigned int block) { + DCHECK_GT(base, 0U); + DCHECK_GT(block, 0U); + return block + base - 1 - (base - 1) % block; +} + +// Fills a MipLevelInfo structure from the base texture dimensions. +static inline void MakeMipLevelInfo(MipLevelInfo *mip_info, + texture::Format format, + unsigned int base_width, + unsigned int base_height, + unsigned int base_depth, + unsigned int level) { + mip_info->block_bpp = texture::GetBytesPerBlock(format); + mip_info->block_size_x = texture::GetBlockSizeX(format); + mip_info->block_size_y = texture::GetBlockSizeY(format); + mip_info->width = RoundToBlockSize( + texture::GetMipMapDimension(base_width, level), mip_info->block_size_x); + mip_info->height = RoundToBlockSize( + texture::GetMipMapDimension(base_height, level), mip_info->block_size_y); + mip_info->depth = texture::GetMipMapDimension(base_depth, level); +} + +// Gets the size in bytes of a mip level. +static inline unsigned int GetMipLevelSize(const MipLevelInfo &mip_info) { + return mip_info.block_bpp * mip_info.width / mip_info.block_size_x * + mip_info.height / mip_info.block_size_y * mip_info.depth; +} + +// Checks that [x .. x+width] is contained in [0 .. mip_width], and that both x +// and width are divisible by block_size, and that width is positive. +static inline bool CheckDimension(unsigned int x, + unsigned int width, + unsigned int mip_width, + unsigned int block_size) { + return x < mip_width && x+width <= mip_width && x % block_size == 0 && + width % block_size == 0 && width > 0; +} + +// Checks that given volume fits into a mip level. +static inline bool CheckVolume(const MipLevelInfo &mip_info, + const Volume &volume) { + return CheckDimension(volume.x, volume.width, mip_info.width, + mip_info.block_size_x) && + CheckDimension(volume.y, volume.height, mip_info.height, + mip_info.block_size_y) && + CheckDimension(volume.z, volume.depth, mip_info.depth, 1); +} + +// Checks whether a volume fully maps a mip level. +static inline bool IsFullVolume(const MipLevelInfo &mip_info, + const Volume &volume) { + return (volume.x == 0) && (volume.y == 0) && (volume.z == 0) && + (volume.width == mip_info.width) && + (volume.height == mip_info.height) && + (volume.depth == mip_info.depth); +} + +// Makes a transfer info from a mip level, a volume and row/slice pitches. +void MakeTransferInfo(TransferInfo *transfer_info, + const MipLevelInfo &mip_level, + const Volume &volume, + unsigned int row_pitch, + unsigned int slice_pitch); + +// Makes a transfer info from a mip level and a volume, considering packed data. +void MakePackedTransferInfo(TransferInfo *transfer_info, + const MipLevelInfo &mip_level, + const Volume &volume); + +// Transfers a volume of texels. +void TransferVolume(const Volume &volume, + const MipLevelInfo &mip_level, + const TransferInfo &dst_transfer_info, + void *dst_data, + const TransferInfo &src_transfer_info, + const void *src_data); + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_CROSS_TEXTURE_UTILS_H_ diff --git a/o3d/gpu/command_buffer/service/x_utils.cc b/o3d/gpu/command_buffer/service/x_utils.cc new file mode 100644 index 0000000..b9eb9d9 --- /dev/null +++ b/o3d/gpu/command_buffer/service/x_utils.cc @@ -0,0 +1,92 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This class implements the XWindowWrapper class. + +#include "gpu/command_buffer/service/precompile.h" +#include "gpu/command_buffer/common/cross/logging.h" +#include "gpu/command_buffer/service/linux/x_utils.h" + +namespace command_buffer { + +bool XWindowWrapper::Initialize() { + XWindowAttributes attributes; + XGetWindowAttributes(display_, window_, &attributes); + XVisualInfo visual_info_template; + visual_info_template.visualid = XVisualIDFromVisual(attributes.visual); + int visual_info_count = 0; + XVisualInfo *visual_info_list = XGetVisualInfo(display_, VisualIDMask, + &visual_info_template, + &visual_info_count); + DCHECK(visual_info_list); + DCHECK_GT(visual_info_count, 0); + context_ = 0; + for (int i = 0; i < visual_info_count; ++i) { + context_ = glXCreateContext(display_, visual_info_list + i, 0, + True); + if (context_) break; + } + XFree(visual_info_list); + if (!context_) { + DLOG(ERROR) << "Couldn't create GL context."; + return false; + } + return true; +} + +bool XWindowWrapper::MakeCurrent() { + if (glXMakeCurrent(display_, window_, context_) != True) { + glXDestroyContext(display_, context_); + context_ = 0; + DLOG(ERROR) << "Couldn't make context current."; + return false; + } + return true; +} + +void XWindowWrapper::Destroy() { + Bool result = glXMakeCurrent(display_, 0, 0); + // glXMakeCurrent isn't supposed to fail when unsetting the context, unless + // we have pending draws on an invalid window - which shouldn't be the case + // here. + DCHECK(result); + if (context_) { + glXDestroyContext(display_, context_); + context_ = 0; + } +} + +void XWindowWrapper::SwapBuffers() { + glXSwapBuffers(display_, window_); +} + +} // namespace command_buffer diff --git a/o3d/gpu/command_buffer/service/x_utils.h b/o3d/gpu/command_buffer/service/x_utils.h new file mode 100644 index 0000000..0d8c26a --- /dev/null +++ b/o3d/gpu/command_buffer/service/x_utils.h @@ -0,0 +1,76 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file declares the XWindowWrapper class. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_X_UTILS_H_ +#define GPU_COMMAND_BUFFER_SERVICE_X_UTILS_H_ + +#include <GL/glx.h> +#include "base/basictypes.h" +#include "gpu/command_buffer/common/cross/logging.h" + +namespace command_buffer { + +// This class is a wrapper around an X Window and associated GL context. It is +// useful to isolate intrusive X headers, since it can be forward declared +// (Window and GLXContext can't). +class XWindowWrapper { + public: + XWindowWrapper(Display *display, Window window) + : display_(display), + window_(window) { + DCHECK(display_); + DCHECK(window_); + } + // Initializes the GL context. + bool Initialize(); + + // Destroys the GL context. + void Destroy(); + + // Makes the GL context current on the current thread. + bool MakeCurrent(); + + // Swaps front and back buffers. + void SwapBuffers(); + + private: + Display *display_; + Window window_; + GLXContext context_; + DISALLOW_COPY_AND_ASSIGN(XWindowWrapper); +}; + +} // namespace command_buffer + +#endif // GPU_COMMAND_BUFFER_SERVICE_X_UTILS_H_ diff --git a/o3d/gpu/gpu.gyp b/o3d/gpu/gpu.gyp new file mode 100644 index 0000000..60b56b8 --- /dev/null +++ b/o3d/gpu/gpu.gyp @@ -0,0 +1,351 @@ +# Copyright (c) 2009 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'includes': [ + '../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'command_buffer_common', + 'type': 'static_library', + 'include_dirs': [ + '..', + '../..', + ], + 'all_dependent_settings': { + 'include_dirs': [ + '..', + '../..', + ], + }, # 'all_dependent_settings' + 'sources': [ + 'command_buffer/common/bitfield_helpers.h', + 'command_buffer/common/cmd_buffer_common.h', + 'command_buffer/common/cmd_buffer_common.cc', + 'command_buffer/common/o3d_cmd_format.h', + 'command_buffer/common/o3d_cmd_format.cc', + 'command_buffer/common/gapi_interface.h', + 'command_buffer/common/logging.h', + 'command_buffer/common/mocks.h', + 'command_buffer/common/resource.cc', + 'command_buffer/common/resource.h', + 'command_buffer/common/types.h', + ], + }, + { + 'target_name': 'command_buffer_common_test', + 'type': 'none', + 'direct_dependent_settings': { + 'sources': [ + 'command_buffer/common/bitfield_helpers_test.cc', + ], + }, + }, + { + 'target_name': 'command_buffer_client', + 'type': 'static_library', + 'dependencies': [ + 'command_buffer_common', + 'np_utils', + ], + 'sources': [ + 'command_buffer/client/cmd_buffer_helper.cc', + 'command_buffer/client/cmd_buffer_helper.h', + 'command_buffer/client/effect_helper.cc', + 'command_buffer/client/effect_helper.h', + 'command_buffer/client/fenced_allocator.cc', + 'command_buffer/client/fenced_allocator.h', + 'command_buffer/client/id_allocator.cc', + 'command_buffer/client/id_allocator.h', + 'command_buffer/client/o3d_cmd_helper.cc', + 'command_buffer/client/o3d_cmd_helper.h', + ], + }, + { + 'target_name': 'command_buffer_client_test', + 'type': 'none', + 'direct_dependent_settings': { + 'sources': [ + 'command_buffer/client/cmd_buffer_helper_test.cc', + 'command_buffer/client/fenced_allocator_test.cc', + 'command_buffer/client/id_allocator_test.cc', + ], + }, + }, + { + 'target_name': 'command_buffer_service', + 'type': 'static_library', + 'all_dependent_settings': { + 'include_dirs': [ + '..', + ], + 'conditions': [ + ['OS == "win" and (renderer == "gl" or cb_service == "gl")', + { + 'include_dirs': [ + '../../<(glewdir)/include', + '../../<(cgdir)/include', + ], + }, + ], + ], + }, # 'all_dependent_settings' + 'dependencies': [ + 'command_buffer_common', + ], + 'sources': [ + 'command_buffer/service/common_decoder.cc', + 'command_buffer/service/common_decoder.h', + 'command_buffer/service/cmd_buffer_engine.h', + 'command_buffer/service/cmd_parser.cc', + 'command_buffer/service/cmd_parser.h', + 'command_buffer/service/effect_utils.cc', + 'command_buffer/service/effect_utils.h', + 'command_buffer/service/gapi_decoder.cc', + 'command_buffer/service/gapi_decoder.h', + 'command_buffer/service/mocks.h', + 'command_buffer/service/precompile.cc', + 'command_buffer/service/precompile.h', + 'command_buffer/service/resource.cc', + 'command_buffer/service/resource.h', + 'command_buffer/service/texture_utils.cc', + 'command_buffer/service/texture_utils.h', + ], + 'conditions': [ + ['cb_service == "d3d9"', + { + 'include_dirs': [ + '$(DXSDK_DIR)/Include', + ], + 'all_dependent_settings': { + 'include_dirs': [ + '$(DXSDK_DIR)/Include', + ], + 'link_settings': { + 'libraries': [ + '"$(DXSDK_DIR)/Lib/x86/DxErr.lib"', + ], + }, + }, # 'all_dependent_settings' + 'sources': [ + 'command_buffer/service/d3d9_utils.h', + 'command_buffer/service/effect_d3d9.cc', + 'command_buffer/service/effect_d3d9.h', + 'command_buffer/service/gapi_d3d9.cc', + 'command_buffer/service/gapi_d3d9.h', + 'command_buffer/service/geometry_d3d9.cc', + 'command_buffer/service/geometry_d3d9.h', + 'command_buffer/service/render_surface_d3d9.cc', + 'command_buffer/service/render_surface_d3d9.h', + 'command_buffer/service/sampler_d3d9.cc', + 'command_buffer/service/sampler_d3d9.h', + 'command_buffer/service/states_d3d9.cc', + 'command_buffer/service/texture_d3d9.cc', + 'command_buffer/service/texture_d3d9.h', + ], # 'sources' + }, + ], + ['cb_service == "gl"', + { + 'dependencies': [ + '../build/libs.gyp:gl_libs', + '../build/libs.gyp:cg_libs', + ], + 'sources': [ + 'command_buffer/service/effect_gl.cc', + 'command_buffer/service/effect_gl.h', + 'command_buffer/service/gapi_gl.cc', + 'command_buffer/service/gapi_gl.h', + 'command_buffer/service/geometry_gl.cc', + 'command_buffer/service/geometry_gl.h', + 'command_buffer/service/gl_utils.h', + 'command_buffer/service/render_surface_gl.cc', + 'command_buffer/service/render_surface_gl.h', + 'command_buffer/service/sampler_gl.cc', + 'command_buffer/service/sampler_gl.h', + 'command_buffer/service/states_gl.cc', + 'command_buffer/service/texture_gl.cc', + 'command_buffer/service/texture_gl.h', + ], # 'sources' + }, + ], + ['OS == "linux"', + { + 'sources': [ + 'command_buffer/service/linux/x_utils.cc', + 'command_buffer/service/linux/x_utils.h', + ], + }, + ], + ], # 'conditions' + }, + { + 'target_name': 'command_buffer_service_test', + 'type': 'none', + 'direct_dependent_settings': { + 'sources': [ + 'command_buffer/service/cmd_parser_test.cc', + 'command_buffer/service/resource_test.cc', + ], + }, + }, + { + 'target_name': 'np_utils', + 'type': '<(library)', + 'dependencies': [ + '../../base/base.gyp:base', + '../build/o3d_in_chrome.gyp:o3d_in_chrome', + ], + 'include_dirs': [ + '..', + '../..', + '../../third_party/npapi', + ], + 'all_dependent_settings': { + 'include_dirs': [ + '..', + '../..', + '../../third_party/npapi', + ], + }, # 'all_dependent_settings' + 'sources': [ + 'np_utils/default_np_object.h', + 'np_utils/dynamic_np_object.cc', + 'np_utils/dynamic_np_object.h', + 'np_utils/np_browser.cc', + 'np_utils/np_browser.h', + 'np_utils/np_browser_mock.h', + 'np_utils/np_browser_stub.cc', + 'np_utils/np_browser_stub.h', + 'np_utils/np_class.h', + 'np_utils/np_dispatcher.cc', + 'np_utils/np_dispatcher.h', + 'np_utils/np_dispatcher_specializations.h', + 'np_utils/np_headers.h', + 'np_utils/np_object_mock.h', + 'np_utils/np_object_pointer.h', + 'np_utils/np_plugin_object.h', + 'np_utils/np_plugin_object_mock.h', + 'np_utils/np_plugin_object_factory.cc', + 'np_utils/np_plugin_object_factory.h', + 'np_utils/np_plugin_object_factory_mock.h', + 'np_utils/np_utils.cc', + 'np_utils/np_utils.h', + 'np_utils/webkit_browser.h', + ], + }, + + # This is a standalone executable until O3D is fully moved over to using + # gyp. At that point these can become part of the regular O3D unit tests. + { + 'target_name': 'np_utils_unittests', + 'type': 'executable', + 'dependencies': [ + 'np_utils', + '../../testing/gmock.gyp:gmock', + '../../testing/gmock.gyp:gmockmain', + '../../testing/gtest.gyp:gtest', + ], + 'include_dirs': [ + '../..', + ], + 'all_dependent_settings': { + 'include_dirs': [ + '../..', + ], + }, # 'all_dependent_settings' + 'sources': [ + 'np_utils/dispatched_np_object_unittest.cc', + 'np_utils/dynamic_np_object_unittest.cc', + 'np_utils/np_class_unittest.cc', + 'np_utils/np_object_pointer_unittest.cc', + 'np_utils/np_utils_unittest.cc', + ], + }, + + # These can eventually be merged back into the gpu_plugin target. There + # separated for now so O3D can statically link against them and use command + # buffers in-process without the GPU plugin. + { + 'target_name': 'command_buffer', + 'type': '<(library)', + 'dependencies': [ + '../../base/base.gyp:base', + 'command_buffer_service', + 'np_utils', + ], + 'all_dependent_settings': { + 'include_dirs': [ + '../..', + ], + }, # 'all_dependent_settings' + 'sources': [ + 'gpu_plugin/command_buffer.cc', + 'gpu_plugin/command_buffer.h', + 'gpu_plugin/command_buffer_mock.h', + 'gpu_plugin/gpu_processor.h', + 'gpu_plugin/gpu_processor.cc', + 'gpu_plugin/gpu_processor_mock.h', + 'gpu_plugin/gpu_processor_win.cc', + ], + }, + + { + 'target_name': 'gpu_plugin', + 'type': '<(library)', + 'dependencies': [ + '../../base/base.gyp:base', + 'command_buffer', + 'np_utils', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'gpu_plugin/gpu_plugin.cc', + 'gpu_plugin/gpu_plugin.h', + 'gpu_plugin/gpu_plugin_object.cc', + 'gpu_plugin/gpu_plugin_object.h', + 'gpu_plugin/gpu_plugin_object_win.cc', + 'gpu_plugin/gpu_plugin_object_factory.cc', + 'gpu_plugin/gpu_plugin_object_factory.h', + ], + }, + + # This is a standalone executable until O3D is fully moved over to using + # gyp. At that point these can become part of the regular O3D unit tests. + { + 'target_name': 'gpu_plugin_unittests', + 'type': 'executable', + 'dependencies': [ + 'command_buffer_service', + 'gpu_plugin', + 'np_utils', + '../../testing/gmock.gyp:gmock', + '../../testing/gmock.gyp:gmockmain', + '../../testing/gtest.gyp:gtest', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'gpu_plugin/command_buffer_unittest.cc', + 'gpu_plugin/gpu_plugin_unittest.cc', + 'gpu_plugin/gpu_plugin_object_unittest.cc', + 'gpu_plugin/gpu_plugin_object_factory_unittest.cc', + 'gpu_plugin/gpu_processor_unittest.cc', + ], + }, + ] +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/o3d/gpu/gpu_plugin/command_buffer.cc b/o3d/gpu/gpu_plugin/command_buffer.cc new file mode 100644 index 0000000..b293693 --- /dev/null +++ b/o3d/gpu/gpu_plugin/command_buffer.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/command_buffer.h" + +using ::base::SharedMemory; + +namespace gpu_plugin { + +CommandBuffer::CommandBuffer(NPP npp) + : npp_(npp), + size_(0), + get_offset_(0), + put_offset_(0), + token_(0), + parse_error_(0), + error_status_(false) { + // Element zero is always NULL. + registered_objects_.push_back(linked_ptr<SharedMemory>()); +} + +CommandBuffer::~CommandBuffer() { +} + +bool CommandBuffer::Initialize(::base::SharedMemory* ring_buffer) { + DCHECK(ring_buffer); + + // Fail if already initialized. + if (ring_buffer_.get()) + return false; + + size_t size_in_bytes = ring_buffer->max_size(); + size_ = size_in_bytes / sizeof(int32); + ring_buffer_.reset(ring_buffer); + + return true; +} + +SharedMemory* CommandBuffer::GetRingBuffer() { + return ring_buffer_.get(); +} + +int32 CommandBuffer::GetSize() { + return size_; +} + +int32 CommandBuffer::SyncOffsets(int32 put_offset) { + if (put_offset < 0 || put_offset >= size_) + return -1; + + put_offset_ = put_offset; + + if (put_offset_change_callback_.get()) { + put_offset_change_callback_->Run(); + } + + return get_offset_; +} + +int32 CommandBuffer::GetGetOffset() { + return get_offset_; +} + +void CommandBuffer::SetGetOffset(int32 get_offset) { + DCHECK(get_offset >= 0 && get_offset < size_); + get_offset_ = get_offset; +} + +int32 CommandBuffer::GetPutOffset() { + return put_offset_; +} + +void CommandBuffer::SetPutOffsetChangeCallback(Callback0::Type* callback) { + put_offset_change_callback_.reset(callback); +} + +int32 CommandBuffer::CreateTransferBuffer(size_t size) { + linked_ptr<SharedMemory> buffer(new SharedMemory); + if (!buffer->Create(std::wstring(), false, false, size)) + return -1; + + if (unused_registered_object_elements_.empty()) { + // Check we haven't exceeded the range that fits in a 32-bit integer. + int32 handle = static_cast<int32>(registered_objects_.size()); + if (handle != registered_objects_.size()) + return -1; + + registered_objects_.push_back(buffer); + return handle; + } + + int32 handle = *unused_registered_object_elements_.begin(); + unused_registered_object_elements_.erase( + unused_registered_object_elements_.begin()); + DCHECK(!registered_objects_[handle].get()); + registered_objects_[handle] = buffer; + return handle; +} + +void CommandBuffer::DestroyTransferBuffer(int32 handle) { + if (handle <= 0) + return; + + if (static_cast<size_t>(handle) >= registered_objects_.size()) + return; + + registered_objects_[handle].reset(); + unused_registered_object_elements_.insert(handle); + + // Remove all null objects from the end of the vector. This allows the vector + // to shrink when, for example, all objects are unregistered. Note that this + // loop never removes element zero, which is always NULL. + while (registered_objects_.size() > 1 && !registered_objects_.back().get()) { + registered_objects_.pop_back(); + unused_registered_object_elements_.erase( + static_cast<int32>(registered_objects_.size())); + } +} + +::base::SharedMemory* CommandBuffer::GetTransferBuffer(int32 handle) { + if (handle < 0) + return NULL; + + if (static_cast<size_t>(handle) >= registered_objects_.size()) + return NULL; + + return registered_objects_[handle].get(); +} + +int32 CommandBuffer::ResetParseError() { + int32 last_error = parse_error_; + parse_error_ = 0; + return last_error; +} + +void CommandBuffer::SetParseError(int32 parse_error) { + if (parse_error_ == 0) { + parse_error_ = parse_error; + } +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/command_buffer.h b/o3d/gpu/gpu_plugin/command_buffer.h new file mode 100644 index 0000000..c92e0f3 --- /dev/null +++ b/o3d/gpu/gpu_plugin/command_buffer.h @@ -0,0 +1,130 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_GPU_PLUGIN_COMMAND_BUFFER_H_ +#define GPU_GPU_PLUGIN_COMMAND_BUFFER_H_ + +#include <set> +#include <vector> + +#include "base/linked_ptr.h" +#include "base/scoped_ptr.h" +#include "base/shared_memory.h" +#include "base/task.h" +#include "gpu/np_utils/default_np_object.h" +#include "gpu/np_utils/np_dispatcher.h" + +namespace gpu_plugin { + +// An NPObject that implements a shared memory command buffer and a synchronous +// API to manage the put and get pointers. +class CommandBuffer : public DefaultNPObject<NPObject> { + public: + explicit CommandBuffer(NPP npp); + virtual ~CommandBuffer(); + + // Initialize the command buffer with the given ring buffer. Takes ownership + // of ring buffer. + virtual bool Initialize(::base::SharedMemory* ring_buffer); + + // Gets the shared memory ring buffer object for the command buffer. + virtual ::base::SharedMemory* GetRingBuffer(); + + virtual int32 GetSize(); + + // The writer calls this to update its put offset. This function returns the + // reader's most recent get offset. Does not return until after the put offset + // change callback has been invoked. Returns -1 if the put offset is invalid. + virtual int32 SyncOffsets(int32 put_offset); + + // Returns the current get offset. This can be called from any thread. + virtual int32 GetGetOffset(); + + // Sets the current get offset. This can be called from any thread. + virtual void SetGetOffset(int32 get_offset); + + // Returns the current put offset. This can be called from any thread. + virtual int32 GetPutOffset(); + + // Sets a callback that should be posted on another thread whenever the put + // offset is changed. The callback must not return until some progress has + // been made (unless the command buffer is empty), i.e. the + // get offset must have changed. It need not process the entire command + // buffer though. This allows concurrency between the writer and the reader + // while giving the writer a means of waiting for the reader to make some + // progress before attempting to write more to the command buffer. Avoiding + // the use of a synchronization primitive like a condition variable to + // synchronize reader and writer reduces the risk of deadlock. + // Takes ownership of callback. The callback is invoked on the plugin thread. + virtual void SetPutOffsetChangeCallback(Callback0::Type* callback); + + // Create a shared memory transfer buffer and return a handle that uniquely + // identifies it or -1 on error. + virtual int32 CreateTransferBuffer(size_t size); + + // Destroy a shared memory transfer buffer and recycle the handle. + virtual void DestroyTransferBuffer(int32 id); + + // Get the shared memory associated with a handle. + virtual ::base::SharedMemory* GetTransferBuffer(int32 handle); + + // Get the current token value. This is used for by the writer to defer + // changes to shared memory objects until the reader has reached a certain + // point in the command buffer. The reader is responsible for updating the + // token value, for example in response to an asynchronous set token command + // embedded in the command buffer. The default token value is zero. + int32 GetToken() { + return token_; + } + + // Allows the reader to update the current token value. + void SetToken(int32 token) { + token_ = token; + } + + // Get the current parse error and reset it to zero. Zero means no error. Non- + // zero means error. The default error status is zero. + int32 ResetParseError(); + + // Allows the reader to set the current parse error. + void SetParseError(int32 parse_error); + + // Returns whether the command buffer is in the error state. + bool GetErrorStatus() { + return error_status_; + } + + // Allows the reader to set the error status. Once in an error state, the + // command buffer cannot recover and ceases to process commands. + void RaiseErrorStatus() { + error_status_ = true; + } + + NP_UTILS_BEGIN_DISPATCHER_CHAIN(CommandBuffer, DefaultNPObject<NPObject>) + NP_UTILS_DISPATCHER(GetSize, int32()) + NP_UTILS_DISPATCHER(SyncOffsets, int32(int32 get_offset)) + NP_UTILS_DISPATCHER(GetGetOffset, int32()); + NP_UTILS_DISPATCHER(GetPutOffset, int32()); + NP_UTILS_DISPATCHER(GetToken, int32()); + NP_UTILS_DISPATCHER(ResetParseError, int32()); + NP_UTILS_DISPATCHER(GetErrorStatus, bool()); + NP_UTILS_END_DISPATCHER_CHAIN + + private: + NPP npp_; + scoped_ptr< ::base::SharedMemory> ring_buffer_; + int32 size_; + int32 get_offset_; + int32 put_offset_; + scoped_ptr<Callback0::Type> put_offset_change_callback_; + std::vector<linked_ptr< ::base::SharedMemory> > registered_objects_; + std::set<int32> unused_registered_object_elements_; + int32 token_; + int32 parse_error_; + bool error_status_; +}; + +} // namespace gpu_plugin + +#endif // GPU_GPU_PLUGIN_COMMAND_BUFFER_H_ diff --git a/o3d/gpu/gpu_plugin/command_buffer_mock.h b/o3d/gpu/gpu_plugin/command_buffer_mock.h new file mode 100644 index 0000000..bb55f52 --- /dev/null +++ b/o3d/gpu/gpu_plugin/command_buffer_mock.h @@ -0,0 +1,42 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_GPU_PLUGIN_COMMAND_BUFFER_MOCK_H_ +#define GPU_GPU_PLUGIN_COMMAND_BUFFER_MOCK_H_ + +#include "gpu/gpu_plugin/command_buffer.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace gpu_plugin { + +// An NPObject that implements a shared memory command buffer and a synchronous +// API to manage the put and get pointers. +class MockCommandBuffer : public CommandBuffer { + public: + explicit MockCommandBuffer(NPP npp) : CommandBuffer(npp) { + ON_CALL(*this, GetRingBuffer()) + .WillByDefault(testing::Return(static_cast<::base::SharedMemory*>(NULL))); + ON_CALL(*this, GetTransferBuffer(testing::_)) + .WillByDefault(testing::Return(static_cast<::base::SharedMemory*>(NULL))); + } + + MOCK_METHOD1(Initialize, bool(::base::SharedMemory* ring_buffer)); + MOCK_METHOD0(GetRingBuffer, ::base::SharedMemory*()); + MOCK_METHOD0(GetSize, int32()); + MOCK_METHOD1(SyncOffsets, int32(int32 put_offset)); + MOCK_METHOD0(GetGetOffset, int32()); + MOCK_METHOD1(SetGetOffset, void(int32 get_offset)); + MOCK_METHOD0(GetPutOffset, int32()); + MOCK_METHOD1(SetPutOffsetChangeCallback, void(Callback0::Type* callback)); + MOCK_METHOD1(CreateTransferBuffer, int32(size_t size)); + MOCK_METHOD1(DestroyTransferBuffer, void(int32 handle)); + MOCK_METHOD1(GetTransferBuffer, ::base::SharedMemory*(int32 handle)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockCommandBuffer); +}; + +} // namespace gpu_plugin + +#endif // GPU_GPU_PLUGIN_COMMAND_BUFFER_MOCK_H_ diff --git a/o3d/gpu/gpu_plugin/command_buffer_unittest.cc b/o3d/gpu/gpu_plugin/command_buffer_unittest.cc new file mode 100644 index 0000000..00e5640 --- /dev/null +++ b/o3d/gpu/gpu_plugin/command_buffer_unittest.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2006-2008 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 "base/thread.h" +#include "gpu/gpu_plugin/command_buffer.h" +#include "gpu/np_utils/np_browser_mock.h" +#include "gpu/np_utils/dynamic_np_object.h" +#include "gpu/np_utils/np_object_mock.h" +#include "gpu/np_utils/np_object_pointer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +using base::SharedMemory; +using testing::_; +using testing::DoAll; +using testing::Return; +using testing::SetArgumentPointee; +using testing::StrictMock; + +namespace gpu_plugin { + +class CommandBufferTest : public testing::Test { + protected: + virtual void SetUp() { + command_buffer_ = NPCreateObject<CommandBuffer>(NULL); + } + + MockNPBrowser mock_browser_; + NPObjectPointer<CommandBuffer> command_buffer_; +}; + +TEST_F(CommandBufferTest, NullRingBufferByDefault) { + EXPECT_TRUE(NULL == command_buffer_->GetRingBuffer()); +} + +TEST_F(CommandBufferTest, InitializesCommandBuffer) { + SharedMemory* ring_buffer = new SharedMemory; + EXPECT_TRUE(ring_buffer->Create(std::wstring(), false, false, 1024)); + EXPECT_TRUE(command_buffer_->Initialize(ring_buffer)); + EXPECT_TRUE(ring_buffer == command_buffer_->GetRingBuffer()); + EXPECT_EQ(256, command_buffer_->GetSize()); +} + +TEST_F(CommandBufferTest, InitializeFailsSecondTime) { + SharedMemory* ring_buffer = new SharedMemory; + EXPECT_TRUE(command_buffer_->Initialize(ring_buffer)); + EXPECT_FALSE(command_buffer_->Initialize(ring_buffer)); +} + +TEST_F(CommandBufferTest, GetAndPutOffsetsDefaultToZero) { + EXPECT_EQ(0, command_buffer_->GetGetOffset()); + EXPECT_EQ(0, command_buffer_->GetPutOffset()); +} + +class MockCallback : public CallbackRunner<Tuple0> { + public: + MOCK_METHOD1(RunWithParams, void(const Tuple0&)); +}; + +TEST_F(CommandBufferTest, CanSyncGetAndPutOffset) { + SharedMemory* ring_buffer = new SharedMemory; + ring_buffer->Create(std::wstring(), false, false, 1024); + + EXPECT_TRUE(command_buffer_->Initialize(ring_buffer)); + + StrictMock<MockCallback>* put_offset_change_callback = + new StrictMock<MockCallback>; + command_buffer_->SetPutOffsetChangeCallback(put_offset_change_callback); + + EXPECT_CALL(*put_offset_change_callback, RunWithParams(_)); + EXPECT_EQ(0, command_buffer_->SyncOffsets(2)); + EXPECT_EQ(2, command_buffer_->GetPutOffset()); + + EXPECT_CALL(*put_offset_change_callback, RunWithParams(_)); + EXPECT_EQ(0, command_buffer_->SyncOffsets(4)); + EXPECT_EQ(4, command_buffer_->GetPutOffset()); + + command_buffer_->SetGetOffset(2); + EXPECT_EQ(2, command_buffer_->GetGetOffset()); + EXPECT_CALL(*put_offset_change_callback, RunWithParams(_)); + EXPECT_EQ(2, command_buffer_->SyncOffsets(6)); + + EXPECT_EQ(-1, command_buffer_->SyncOffsets(-1)); + EXPECT_EQ(-1, command_buffer_->SyncOffsets(1024)); +} + +TEST_F(CommandBufferTest, ZeroHandleMapsToNull) { + EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(0)); +} + +TEST_F(CommandBufferTest, NegativeHandleMapsToNull) { + EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(-1)); +} + +TEST_F(CommandBufferTest, OutOfRangeHandleMapsToNull) { + EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(1)); +} + +TEST_F(CommandBufferTest, CanCreateTransferBuffers) { + int32 handle = command_buffer_->CreateTransferBuffer(1024); + EXPECT_EQ(1, handle); + SharedMemory* buffer = command_buffer_->GetTransferBuffer(handle); + ASSERT_TRUE(NULL != buffer); + EXPECT_EQ(1024, buffer->max_size()); +} + +TEST_F(CommandBufferTest, CreateTransferBufferReturnsDistinctHandles) { + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); +} + +TEST_F(CommandBufferTest, CreateTransferBufferReusesUnregisteredHandles) { + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); + EXPECT_EQ(2, command_buffer_->CreateTransferBuffer(1024)); + command_buffer_->DestroyTransferBuffer(1); + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); + EXPECT_EQ(3, command_buffer_->CreateTransferBuffer(1024)); +} + +TEST_F(CommandBufferTest, CannotUnregisterHandleZero) { + command_buffer_->DestroyTransferBuffer(0); + EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(0)); + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); +} + +TEST_F(CommandBufferTest, CannotUnregisterNegativeHandles) { + command_buffer_->DestroyTransferBuffer(-1); + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); +} + +TEST_F(CommandBufferTest, CannotUnregisterUnregisteredHandles) { + command_buffer_->DestroyTransferBuffer(1); + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); +} + +// Testing this case specifically because there is an optimization that takes +// a different code path in this case. +TEST_F(CommandBufferTest, UnregistersLastRegisteredHandle) { + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); + command_buffer_->DestroyTransferBuffer(1); + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); +} + +// Testing this case specifically because there is an optimization that takes +// a different code path in this case. +TEST_F(CommandBufferTest, UnregistersTwoLastRegisteredHandles) { + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); + EXPECT_EQ(2, command_buffer_->CreateTransferBuffer(1024)); + command_buffer_->DestroyTransferBuffer(2); + command_buffer_->DestroyTransferBuffer(1); + EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024)); +} + +TEST_F(CommandBufferTest, DefaultTokenIsZero) { + EXPECT_EQ(0, command_buffer_->GetToken()); +} + +TEST_F(CommandBufferTest, CanSetToken) { + command_buffer_->SetToken(7); + EXPECT_EQ(7, command_buffer_->GetToken()); +} + +TEST_F(CommandBufferTest, DefaultParseErrorIsNoError) { + EXPECT_EQ(0, command_buffer_->ResetParseError()); +} + +TEST_F(CommandBufferTest, CanSetAndResetParseError) { + command_buffer_->SetParseError(1); + EXPECT_EQ(1, command_buffer_->ResetParseError()); + EXPECT_EQ(0, command_buffer_->ResetParseError()); +} + +TEST_F(CommandBufferTest, DefaultErrorStatusIsFalse) { + EXPECT_FALSE(command_buffer_->GetErrorStatus()); +} + +TEST_F(CommandBufferTest, CanRaiseErrorStatus) { + command_buffer_->RaiseErrorStatus(); + EXPECT_TRUE(command_buffer_->GetErrorStatus()); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_plugin.cc b/o3d/gpu/gpu_plugin/gpu_plugin.cc new file mode 100644 index 0000000..d8cfb58 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/gpu_plugin.h" +#include "gpu/gpu_plugin/gpu_plugin_object_factory.h" +#include "gpu/np_utils/np_browser.h" +#include "gpu/np_utils/np_plugin_object.h" +#include "gpu/np_utils/np_plugin_object_factory.h" + +#if defined(O3D_IN_CHROME) +#include "webkit/glue/plugins/nphostapi.h" +#else +#include "o3d/third_party/npapi/include/npfunctions.h" +#endif + +namespace gpu_plugin { + +// Definitions of NPAPI plugin entry points. + +namespace { +NPBrowser* g_browser; +GPUPluginObjectFactory g_plugin_object_factory; + +NPError NPP_New(NPMIMEType plugin_type, NPP instance, + uint16 mode, int16 argc, char* argn[], + char* argv[], NPSavedData* saved) { + if (!instance) + return NPERR_INVALID_INSTANCE_ERROR; + + PluginObject* plugin_object = + NPPluginObjectFactory::get()->CreatePluginObject(instance, plugin_type); + if (!plugin_object) + return NPERR_GENERIC_ERROR; + + instance->pdata = plugin_object; + + NPError error = plugin_object->New(plugin_type, argc, argn, argv, saved); + if (error != NPERR_NO_ERROR) { + plugin_object->Release(); + } + + return error; +} + +NPError NPP_Destroy(NPP instance, NPSavedData** saved) { + if (!instance) + return NPERR_INVALID_INSTANCE_ERROR; + + PluginObject* plugin_object = static_cast<PluginObject*>(instance->pdata); + NPError error = plugin_object->Destroy(saved); + + if (error == NPERR_NO_ERROR) { + plugin_object->Release(); + } + + return error; +} + +NPError NPP_SetWindow(NPP instance, NPWindow* window) { + if (!instance) + return NPERR_INVALID_INSTANCE_ERROR; + + PluginObject* plugin_object = static_cast<PluginObject*>(instance->pdata); + return plugin_object->SetWindow(window); +} + +int16 NPP_HandleEvent(NPP instance, void* event) { + if (!instance) + return 0; + + PluginObject* plugin_object = static_cast<PluginObject*>(instance->pdata); + return plugin_object->HandleEvent(static_cast<NPEvent*>(event)); +} + +NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) { + if (!instance) + return NPERR_INVALID_INSTANCE_ERROR; + + PluginObject* plugin_object = static_cast<PluginObject*>(instance->pdata); + switch (variable) { + case NPPVpluginScriptableNPObject: + *reinterpret_cast<NPObject**>(value) = + plugin_object->GetScriptableNPObject(); + return NPERR_NO_ERROR; + default: + return NPERR_GENERIC_ERROR; + } +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) { + return NPERR_NO_ERROR; +} +} + +NPError NP_GetEntryPoints(NPPluginFuncs* funcs) { + funcs->newp = NPP_New; + funcs->destroy = NPP_Destroy; + funcs->setwindow = NPP_SetWindow; + funcs->event = NPP_HandleEvent; + funcs->getvalue = NPP_GetValue; + funcs->setvalue = NPP_SetValue; + return NPERR_NO_ERROR; +} + +#if defined(OS_LINUX) +NPError API_CALL NP_Initialize(NPNetscapeFuncs *browser_funcs, + NPPluginFuncs* plugin_funcs) { +#else +NPError NP_Initialize(NPNetscapeFuncs *browser_funcs) { +#endif + if (!browser_funcs) + return NPERR_INVALID_FUNCTABLE_ERROR; + + if (g_browser) + return NPERR_GENERIC_ERROR; + +#if defined(OS_LINUX) + NP_GetEntryPoints(plugin_funcs); +#endif + + g_browser = new NPBrowser(browser_funcs); + + return NPERR_NO_ERROR; +} + +NPError NP_Shutdown() { + if (!g_browser) + return NPERR_GENERIC_ERROR; + + delete g_browser; + g_browser = NULL; + + return NPERR_NO_ERROR; +} +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_plugin.h b/o3d/gpu/gpu_plugin/gpu_plugin.h new file mode 100644 index 0000000..0f90f77 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin.h @@ -0,0 +1,30 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_GPU_PLUGIN_GPU_PLUGIN_H_ +#define GPU_GPU_PLUGIN_GPU_PLUGIN_H_ + +#include "gpu/np_utils/np_headers.h" + +typedef struct _NPPluginFuncs NPPluginFuncs; +typedef struct _NPNetscapeFuncs NPNetscapeFuncs; + +namespace gpu_plugin { + +// Declarations of NPAPI plugin entry points. + +NPError NP_GetEntryPoints(NPPluginFuncs* funcs); + +#if defined(OS_LINUX) +NPError NP_Initialize(NPNetscapeFuncs *browser_funcs, + NPPluginFuncs* plugin_funcs); +#else +NPError NP_Initialize(NPNetscapeFuncs* browser_funcs); +#endif + +NPError NP_Shutdown(); + +} // namespace gpu_plugin + +#endif // GPU_GPU_PLUGIN_GPU_PLUGIN_H_ diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_object.cc b/o3d/gpu/gpu_plugin/gpu_plugin_object.cc new file mode 100644 index 0000000..6a2bd6c --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_object.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2006-2008 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 <stdlib.h> + +#include "base/logging.h" +#include "gpu/np_utils/np_utils.h" +#include "gpu/gpu_plugin/gpu_plugin_object.h" +#include "gpu/gpu_plugin/gpu_processor.h" + +using ::base::SharedMemory; + +namespace gpu_plugin { + +const NPUTF8 GPUPluginObject::kPluginType[] = + "application/vnd.google.chrome.gpu-plugin"; + +GPUPluginObject::GPUPluginObject(NPP npp) + : npp_(npp), + status_(kWaitingForNew), + command_buffer_(NPCreateObject<CommandBuffer>(npp)), + processor_(new GPUProcessor(npp, command_buffer_.Get())) { + memset(&window_, 0, sizeof(window_)); +} + +NPError GPUPluginObject::New(NPMIMEType plugin_type, + int16 argc, + char* argn[], + char* argv[], + NPSavedData* saved) { + if (status_ != kWaitingForNew) + return NPERR_GENERIC_ERROR; + + status_ = kWaitingForSetWindow; + + return NPERR_NO_ERROR; +} + +NPError GPUPluginObject::SetWindow(NPWindow* new_window) { + if (status_ == kWaitingForNew || status_ == kDestroyed) + return NPERR_GENERIC_ERROR; + + // PlatformSpecificSetWindow advances the status depending on what happens. + NPError error = PlatformSpecificSetWindow(new_window); + if (error == NPERR_NO_ERROR) { + window_ = *new_window; + + if (event_sync_.Get()) { + NPInvokeVoid(npp_, + event_sync_, + "resize", + static_cast<int32>(window_.width), + static_cast<int32>(window_.height)); + } + } else { + memset(&window_, 0, sizeof(window_)); + } + + return error; +} + +int16 GPUPluginObject::HandleEvent(NPEvent* event) { + return 0; +} + +NPError GPUPluginObject::Destroy(NPSavedData** saved) { + if (status_ == kWaitingForNew || status_ == kDestroyed) + return NPERR_GENERIC_ERROR; + + if (command_buffer_.Get()) { + command_buffer_->SetPutOffsetChangeCallback(NULL); + } + + status_ = kDestroyed; + + return NPERR_NO_ERROR; +} + +void GPUPluginObject::Release() { + DCHECK(status_ == kWaitingForNew || status_ == kDestroyed); + NPBrowser::get()->ReleaseObject(this); +} + +NPObject*GPUPluginObject::GetScriptableNPObject() { + NPBrowser::get()->RetainObject(this); + return this; +} + +NPObjectPointer<NPObject> GPUPluginObject::OpenCommandBuffer() { + if (status_ == kInitializationSuccessful) + return command_buffer_; + + // SetWindow must have been called before OpenCommandBuffer. + // PlatformSpecificSetWindow advances the status to + // kWaitingForOpenCommandBuffer. + if (status_ != kWaitingForOpenCommandBuffer) + return NPObjectPointer<NPObject>(); + + scoped_ptr<SharedMemory> ring_buffer(new SharedMemory); + if (!ring_buffer->Create(std::wstring(), false, false, kCommandBufferSize)) + return NPObjectPointer<NPObject>(); + + if (command_buffer_->Initialize(ring_buffer.release())) { + if (processor_->Initialize(static_cast<HWND>(window_.window))) { + command_buffer_->SetPutOffsetChangeCallback( + NewCallback(processor_.get(), + &GPUProcessor::ProcessCommands)); + status_ = kInitializationSuccessful; + return command_buffer_; + } + } + + return NPObjectPointer<CommandBuffer>(); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_object.h b/o3d/gpu/gpu_plugin/gpu_plugin_object.h new file mode 100644 index 0000000..09f612d --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_object.h @@ -0,0 +1,132 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_GPU_PLUGIN_GPU_PLUGIN_OBJECT_H_ +#define GPU_GPU_PLUGIN_GPU_PLUGIN_OBJECT_H_ + +#include <string> + +#include "base/ref_counted.h" +#include "base/thread.h" +#include "gpu/gpu_plugin/command_buffer.h" +#include "gpu/gpu_plugin/gpu_processor.h" +#include "gpu/np_utils/default_np_object.h" +#include "gpu/np_utils/np_dispatcher.h" +#include "gpu/np_utils/np_headers.h" +#include "gpu/np_utils/np_plugin_object.h" +#include "gpu/np_utils/np_utils.h" + +namespace gpu_plugin { + +// The scriptable object for the GPU plugin. +class GPUPluginObject : public DefaultNPObject<NPObject>, + public PluginObject { + public: + static const int32 kCommandBufferSize = 1024 * 1024; + + enum Status { + // In the state of waiting for the named function to be called to continue + // the initialization sequence. + kWaitingForNew, + kWaitingForSetWindow, + kWaitingForOpenCommandBuffer, + + // Initialization either succeeded or failed. + kInitializationSuccessful, + kInitializationFailed, + + // Destroy has now been called and the plugin object cannot be used. + kDestroyed, + }; + + static const NPUTF8 kPluginType[]; + + explicit GPUPluginObject(NPP npp); + + virtual NPError New(NPMIMEType plugin_type, + int16 argc, + char* argn[], + char* argv[], + NPSavedData* saved); + + virtual NPError SetWindow(NPWindow* new_window); + const NPWindow& GetWindow() { return window_; } + + virtual int16 HandleEvent(NPEvent* event); + + virtual NPError Destroy(NPSavedData** saved); + + virtual void Release(); + + virtual NPObject* GetScriptableNPObject(); + + // Returns the current initialization status. See Status enum. + int32 GetStatus() { + return status_; + } + + // Get the width of the plugin window. + int32 GetWidth() { + return window_.width; + } + + // Get the height of the plugin window. + int32 GetHeight() { + return window_.height; + } + + // Set the object that receives notifications of GPU plugin object events + // such as resize and keyboard and mouse input. + void SetEventSync(NPObjectPointer<NPObject> event_sync) { + event_sync_ = event_sync; + } + + NPObjectPointer<NPObject> GetEventSync() { + return event_sync_; + } + + // Initializes and returns the command buffer object. Returns NULL if the + // command buffer cannot be initialized, for example if the plugin does not + // yet have a window handle. + NPObjectPointer<NPObject> OpenCommandBuffer(); + + // Set the status for testing. + void set_status(Status status) { + status_ = status; + } + + // Replace the default command buffer for testing. + void set_command_buffer( + const NPObjectPointer<CommandBuffer>& command_buffer) { + command_buffer_ = command_buffer; + } + + // Replace the default GPU processor for testing. + void set_gpu_processor(const scoped_refptr<GPUProcessor>& processor) { + processor_ = processor; + } + + NP_UTILS_BEGIN_DISPATCHER_CHAIN(GPUPluginObject, DefaultNPObject<NPObject>) + NP_UTILS_DISPATCHER(GetStatus, int32()); + NP_UTILS_DISPATCHER(GetWidth, int32()); + NP_UTILS_DISPATCHER(GetHeight, int32()); + NP_UTILS_DISPATCHER(SetEventSync, void(NPObjectPointer<NPObject> sync)); + NP_UTILS_DISPATCHER(GetEventSync, NPObjectPointer<NPObject>()); + NP_UTILS_DISPATCHER(OpenCommandBuffer, NPObjectPointer<NPObject>()) + NP_UTILS_END_DISPATCHER_CHAIN + + private: + NPError PlatformSpecificSetWindow(NPWindow* new_window); + + NPP npp_; + Status status_; + NPWindow window_; + NPObjectPointer<CommandBuffer> command_buffer_; + scoped_refptr<GPUProcessor> processor_; + NPObjectPointer<NPObject> event_sync_; +}; + +} // namespace gpu_plugin + +#endif // GPU_GPU_PLUGIN_GPU_PLUGIN_OBJECT_H_ diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_object_factory.cc b/o3d/gpu/gpu_plugin/gpu_plugin_object_factory.cc new file mode 100644 index 0000000..cfdced8 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_object_factory.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/gpu_plugin_object.h" +#include "gpu/gpu_plugin/gpu_plugin_object_factory.h" +#include "gpu/np_utils/np_utils.h" + +namespace gpu_plugin { + +GPUPluginObjectFactory::GPUPluginObjectFactory() { +} + +GPUPluginObjectFactory::~GPUPluginObjectFactory() { +} + +PluginObject* GPUPluginObjectFactory::CreatePluginObject( + NPP npp, + NPMIMEType plugin_type) { + if (strcmp(plugin_type, GPUPluginObject::kPluginType) == 0) { + return NPCreateObject<GPUPluginObject>(npp).ToReturned(); + } + + return NULL; +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_object_factory.h b/o3d/gpu/gpu_plugin/gpu_plugin_object_factory.h new file mode 100644 index 0000000..1a73baf --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_object_factory.h @@ -0,0 +1,26 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_GPU_PLUGIN_GPU_PLUGIN_OBJECT_FACTORY_H_ +#define GPU_GPU_PLUGIN_GPU_PLUGIN_OBJECT_FACTORY_H_ + +#include "gpu/np_utils/np_plugin_object_factory.h" + +namespace gpu_plugin { + +// Plugin object factory for creating the GPUPluginObject. +class GPUPluginObjectFactory : public NPPluginObjectFactory { + public: + GPUPluginObjectFactory(); + virtual ~GPUPluginObjectFactory(); + + virtual PluginObject* CreatePluginObject(NPP npp, NPMIMEType plugin_type); + + private: + DISALLOW_COPY_AND_ASSIGN(GPUPluginObjectFactory); +}; + +} // namespace gpu_plugin + +#endif // GPU_GPU_PLUGIN_GPU_PLUGIN_OBJECT_FACTORY_H_ diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_object_factory_unittest.cc b/o3d/gpu/gpu_plugin/gpu_plugin_object_factory_unittest.cc new file mode 100644 index 0000000..7b006c2 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_object_factory_unittest.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/gpu_plugin_object.h" +#include "gpu/gpu_plugin/gpu_plugin_object_factory.h" +#include "gpu/np_utils/np_browser_stub.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gpu_plugin { + +class PluginObjectFactoryTest : public testing::Test { + protected: + virtual void SetUp() { + factory_ = new GPUPluginObjectFactory; + } + + virtual void TearDown() { + delete factory_; + } + + StubNPBrowser stub_browser_; + GPUPluginObjectFactory* factory_; +}; + +TEST_F(PluginObjectFactoryTest, ReturnsNullForUnknownMimeType) { + PluginObject* plugin_object = factory_->CreatePluginObject( + NULL, "application/unknown"); + EXPECT_TRUE(NULL == plugin_object); +} + +TEST_F(PluginObjectFactoryTest, CreatesGPUPlugin) { + PluginObject* plugin_object = factory_->CreatePluginObject( + NULL, const_cast<NPMIMEType>(GPUPluginObject::kPluginType)); + EXPECT_TRUE(NULL != plugin_object); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_object_unittest.cc b/o3d/gpu/gpu_plugin/gpu_plugin_object_unittest.cc new file mode 100644 index 0000000..2f91225 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_object_unittest.cc @@ -0,0 +1,346 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/command_buffer_mock.h" +#include "gpu/gpu_plugin/gpu_plugin_object.h" +#include "gpu/gpu_plugin/gpu_processor_mock.h" +#include "gpu/np_utils/np_browser_mock.h" +#include "gpu/np_utils/dynamic_np_object.h" +#include "gpu/np_utils/np_object_mock.h" +#include "gpu/np_utils/np_object_pointer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +#if defined(O3D_IN_CHROME) +#include "webkit/glue/plugins/nphostapi.h" +#else +#include "o3d/third_party/npapi/include/npfunctions.h" +#endif + +using ::base::SharedMemory; + +using testing::_; +using testing::DoAll; +using testing::Invoke; +using testing::NotNull; +using testing::Return; +using testing::SetArgumentPointee; +using testing::StrictMock; + +namespace gpu_plugin { + +class MockSystemNPObject : public DefaultNPObject<NPObject> { + public: + explicit MockSystemNPObject(NPP npp) { + } + + MOCK_METHOD1(CreateSharedMemory, NPObjectPointer<NPObject>(int32 size)); + + NP_UTILS_BEGIN_DISPATCHER_CHAIN(MockSystemNPObject, DefaultNPObject<NPObject>) + NP_UTILS_DISPATCHER(CreateSharedMemory, + NPObjectPointer<NPObject>(int32 size)) + NP_UTILS_END_DISPATCHER_CHAIN + + private: + DISALLOW_COPY_AND_ASSIGN(MockSystemNPObject); +}; + +class GPUPluginObjectTest : public testing::Test { + protected: + virtual void SetUp() { + plugin_object_ = NPCreateObject<GPUPluginObject>(NULL); + + command_buffer_ = NPCreateObject<MockCommandBuffer>(NULL); + plugin_object_->set_command_buffer(command_buffer_); + + processor_ = new MockGPUProcessor(NULL, command_buffer_.Get()); + plugin_object_->set_gpu_processor(processor_.get()); + + window_object_ = NPCreateObject<DynamicNPObject>(NULL); + ON_CALL(mock_browser_, GetWindowNPObject(NULL)) + .WillByDefault(Return(window_object_.ToReturned())); + + chromium_object_ = NPCreateObject<DynamicNPObject>(NULL); + NPSetProperty(NULL, window_object_, "chromium", chromium_object_); + + system_object_ = NPCreateObject<StrictMock<MockSystemNPObject> >(NULL); + NPSetProperty(NULL, chromium_object_, "system", system_object_); + } + + MockNPBrowser mock_browser_; + NPObjectPointer<GPUPluginObject> plugin_object_; + NPObjectPointer<MockCommandBuffer> command_buffer_; + scoped_refptr<MockGPUProcessor> processor_; + NPObjectPointer<DynamicNPObject> window_object_; + NPObjectPointer<DynamicNPObject> chromium_object_; + NPObjectPointer<MockSystemNPObject> system_object_; +}; + +namespace { +template <typename T> +void DeleteObject(T* object) { + delete object; +} +} // namespace anonymous + + +TEST_F(GPUPluginObjectTest, CanInstantiateAndDestroyPluginObject) { + EXPECT_EQ(GPUPluginObject::kWaitingForNew, plugin_object_->GetStatus()); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + EXPECT_EQ(GPUPluginObject::kWaitingForSetWindow, plugin_object_->GetStatus()); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); + + EXPECT_EQ(GPUPluginObject::kDestroyed, plugin_object_->GetStatus()); +} + +TEST_F(GPUPluginObjectTest, DestroyFailsIfNotInitialized) { + EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_object_->Destroy(NULL)); +} + +TEST_F(GPUPluginObjectTest, NewFailsIfAlreadyInitialized) { + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + EXPECT_EQ(GPUPluginObject::kWaitingForSetWindow, plugin_object_->GetStatus()); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); + + EXPECT_EQ(GPUPluginObject::kDestroyed, plugin_object_->GetStatus()); +} + +TEST_F(GPUPluginObjectTest, NewFailsIfObjectHasPreviouslyBeenDestroyed) { + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); + + EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + EXPECT_EQ(GPUPluginObject::kDestroyed, plugin_object_->GetStatus()); +} + +TEST_F(GPUPluginObjectTest, WindowIsNullBeforeSetWindowCalled) { + NPWindow window = plugin_object_->GetWindow(); + EXPECT_EQ(NULL, window.window); +} + +TEST_F(GPUPluginObjectTest, CanSetWindow) { + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + NPWindow window = {0}; + window.window = &window; + window.x = 7; + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->SetWindow(&window)); + EXPECT_EQ(0, memcmp(&window, &plugin_object_->GetWindow(), sizeof(window))); + EXPECT_EQ(GPUPluginObject::kWaitingForOpenCommandBuffer, + plugin_object_->GetStatus()); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); +} + +TEST_F(GPUPluginObjectTest, CanGetWindowSize) { + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + NPWindow window = {0}; + window.window = &window; + window.x = 10; + window.y = 10; + window.width = 100; + window.height = 200; + + EXPECT_EQ(0, plugin_object_->GetWidth()); + EXPECT_EQ(0, plugin_object_->GetHeight()); + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->SetWindow(&window)); + EXPECT_EQ(100, plugin_object_->GetWidth()); + EXPECT_EQ(200, plugin_object_->GetHeight()); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); +} + +TEST_F(GPUPluginObjectTest, SetWindowFailsIfNotInitialized) { + NPWindow window = {0}; + EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_object_->SetWindow(&window)); + EXPECT_EQ(GPUPluginObject::kWaitingForNew, plugin_object_->GetStatus()); +} + +TEST_F(GPUPluginObjectTest, CanGetScriptableNPObject) { + NPObject* scriptable_object = plugin_object_->GetScriptableNPObject(); + EXPECT_EQ(plugin_object_.Get(), scriptable_object); + NPBrowser::get()->ReleaseObject(scriptable_object); +} + +TEST_F(GPUPluginObjectTest, OpenCommandBufferReturnsInitializedCommandBuffer) { + EXPECT_CALL(*command_buffer_.Get(), Initialize(NotNull())) + .WillOnce(DoAll(Invoke(DeleteObject<SharedMemory>), + Return(true))); + + EXPECT_CALL(*processor_.get(), Initialize(NULL)) + .WillOnce(Return(true)); + + EXPECT_CALL(*command_buffer_.Get(), SetPutOffsetChangeCallback(NotNull())) + .WillOnce(Invoke(DeleteObject<Callback0::Type>)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + // Set status as though SetWindow has been called. Avoids having to create a + // valid window handle to pass to SetWindow in tests. + plugin_object_->set_status(GPUPluginObject::kWaitingForOpenCommandBuffer); + + EXPECT_EQ(command_buffer_, plugin_object_->OpenCommandBuffer()); + + // Calling OpenCommandBuffer again just returns the existing command buffer. + EXPECT_EQ(command_buffer_, plugin_object_->OpenCommandBuffer()); + + EXPECT_EQ(GPUPluginObject::kInitializationSuccessful, + plugin_object_->GetStatus()); + + EXPECT_CALL(*command_buffer_.Get(), SetPutOffsetChangeCallback(NULL)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); +} + +TEST_F(GPUPluginObjectTest, OpenCommandBufferReturnsNullIfWindowNotReady) { + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + // Set status as though SetWindow has not been called. + plugin_object_->set_status(GPUPluginObject::kWaitingForSetWindow); + + EXPECT_EQ(NPObjectPointer<NPObject>(), plugin_object_->OpenCommandBuffer()); + + EXPECT_EQ(GPUPluginObject::kWaitingForSetWindow, plugin_object_->GetStatus()); +} + + +TEST_F(GPUPluginObjectTest, + OpenCommandBufferReturnsNullIfCommandBufferCannotInitialize) { + EXPECT_CALL(*command_buffer_.Get(), Initialize(NotNull())) + .WillOnce(DoAll(Invoke(DeleteObject<SharedMemory>), + Return(false))); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + // Set status as though SetWindow has been called. Avoids having to create a + // valid window handle to pass to SetWindow in tests. + plugin_object_->set_status(GPUPluginObject::kWaitingForOpenCommandBuffer); + + EXPECT_EQ(NPObjectPointer<NPObject>(), plugin_object_->OpenCommandBuffer()); + + EXPECT_EQ(GPUPluginObject::kWaitingForOpenCommandBuffer, + plugin_object_->GetStatus()); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); +} + +TEST_F(GPUPluginObjectTest, + OpenCommandBufferReturnsNullIGPUProcessorCannotInitialize) { + EXPECT_CALL(*command_buffer_.Get(), Initialize(NotNull())) + .WillOnce(DoAll(Invoke(DeleteObject<SharedMemory>), + Return(true))); + + EXPECT_CALL(*processor_.get(), Initialize(NULL)) + .WillOnce(Return(false)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + // Set status as though SetWindow has been called. Avoids having to create a + // valid window handle to pass to SetWindow in tests. + plugin_object_->set_status(GPUPluginObject::kWaitingForOpenCommandBuffer); + + EXPECT_EQ(NPObjectPointer<NPObject>(), plugin_object_->OpenCommandBuffer()); + + EXPECT_EQ(GPUPluginObject::kWaitingForOpenCommandBuffer, + plugin_object_->GetStatus()); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); +} + +class MockEventSync : public DefaultNPObject<NPObject> { + public: + explicit MockEventSync(NPP npp) { + } + + MOCK_METHOD2(Resize, void(int32 width, int32 height)); + + NP_UTILS_BEGIN_DISPATCHER_CHAIN(MockEventSync, DefaultNPObject<NPObject>) + NP_UTILS_DISPATCHER(Resize, void(int32 width, int32 height)) + NP_UTILS_END_DISPATCHER_CHAIN + + private: + DISALLOW_COPY_AND_ASSIGN(MockEventSync); +}; + +TEST_F(GPUPluginObjectTest, SendsResizeEventOnSetWindow) { + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->New("application/foo", + 0, + NULL, + NULL, + NULL)); + + NPObjectPointer<MockEventSync> event_sync = + NPCreateObject<MockEventSync>(NULL); + plugin_object_->SetEventSync(event_sync); + + EXPECT_CALL(*event_sync.Get(), Resize(100, 200)); + + NPWindow window = {0}; + window.window = &window; + window.x = 10; + window.y = 10; + window.width = 100; + window.height = 200; + + plugin_object_->SetWindow(&window); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_object_->Destroy(NULL)); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_object_win.cc b/o3d/gpu/gpu_plugin/gpu_plugin_object_win.cc new file mode 100644 index 0000000..7108a09 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_object_win.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2008 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 <windows.h> + +#include "gpu/gpu_plugin/gpu_plugin_object.h" +#include "gpu/gpu_plugin/gpu_processor.h" + +namespace gpu_plugin { + +namespace { +const LPCTSTR kPluginObjectProperty = TEXT("GPUPluginObject"); +const LPCTSTR kOriginalWindowProc = TEXT("GPUPluginObjectOriginalWindowProc"); + +LRESULT CALLBACK WindowProc(HWND handle, + UINT message, + WPARAM w_param, + LPARAM l_param) { + return ::DefWindowProc(handle, message, w_param, l_param); +} +} // namespace anonymous + +NPError GPUPluginObject::PlatformSpecificSetWindow(NPWindow* new_window) { + // Detach properties from old window and restore the original window proc. + if (window_.window) { + HWND handle = reinterpret_cast<HWND>(window_.window); + ::RemoveProp(handle, kPluginObjectProperty); + + LONG original_window_proc = reinterpret_cast<LONG>( + ::GetProp(handle, kOriginalWindowProc)); + ::SetWindowLong(handle, GWL_WNDPROC, + original_window_proc); + ::RemoveProp(handle, kOriginalWindowProc); + } + + // Attach properties to new window and set a new window proc. + if (new_window->window) { + HWND handle = reinterpret_cast<HWND>(new_window->window); + ::SetProp(handle, + kPluginObjectProperty, + reinterpret_cast<HANDLE>(this)); + + LONG original_window_proc = ::GetWindowLong(handle, GWL_WNDPROC); + ::SetProp(handle, + kOriginalWindowProc, + reinterpret_cast<HANDLE>(original_window_proc)); + ::SetWindowLong(handle, GWL_WNDPROC, + reinterpret_cast<LONG>(WindowProc)); + + status_ = kWaitingForOpenCommandBuffer; + } else { + status_ = kWaitingForSetWindow; + if (processor_) { + processor_->Destroy(); + } + } + + return NPERR_NO_ERROR; +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_plugin_unittest.cc b/o3d/gpu/gpu_plugin/gpu_plugin_unittest.cc new file mode 100644 index 0000000..7d9ae86 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_plugin_unittest.cc @@ -0,0 +1,305 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/gpu_plugin.h" +#include "gpu/gpu_plugin/gpu_plugin_object.h" +#include "gpu/np_utils/np_object_mock.h" +#include "gpu/np_utils/np_plugin_object_factory_mock.h" +#include "gpu/np_utils/np_plugin_object_mock.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(O3D_IN_CHROME) +#include "webkit/glue/plugins/nphostapi.h" +#else +#include "o3d/third_party/npapi/include/npfunctions.h" +#endif + +#if defined(OS_LINUX) +#define INITIALIZE_PLUGIN_FUNCS , &plugin_funcs_ +#else +#define INITIALIZE_PLUGIN_FUNCS +#endif + +using testing::_; +using testing::DoAll; +using testing::NiceMock; +using testing::Return; +using testing::SetArgumentPointee; +using testing::StrictMock; + +namespace gpu_plugin { + +class GPUPluginTest : public testing::Test { + protected: + virtual void SetUp() { + memset(&npp_, 0, sizeof(npp_)); + memset(&browser_funcs_, 0, sizeof(browser_funcs_)); + memset(&plugin_funcs_, 0, sizeof(plugin_funcs_)); + + plugin_object_factory_ = new StrictMock<MockPluginObjectFactory>; + + np_class_ = NPGetClass<StrictMock<MockNPObject> >(); + } + + virtual void TearDown() { + delete plugin_object_factory_; + } + + NPP_t npp_; + NPNetscapeFuncs browser_funcs_; + NPPluginFuncs plugin_funcs_; + MockPluginObjectFactory* plugin_object_factory_; + const NPClass* np_class_; +}; + +TEST_F(GPUPluginTest, GetEntryPointsSetsNeededFunctionPointers) { +#if defined(OS_LINUX) + NPError error = gpu_plugin::NP_Initialize(&browser_funcs_, + &plugin_funcs_); + gpu_plugin::NP_Shutdown(); +#else + NPError error = gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); +#endif + + EXPECT_EQ(NPERR_NO_ERROR, error); + EXPECT_TRUE(NULL != plugin_funcs_.newp); + EXPECT_TRUE(NULL != plugin_funcs_.destroy); + EXPECT_TRUE(NULL != plugin_funcs_.setwindow); + EXPECT_TRUE(NULL != plugin_funcs_.event); + EXPECT_TRUE(NULL != plugin_funcs_.getvalue); + EXPECT_TRUE(NULL != plugin_funcs_.setvalue); +} + +TEST_F(GPUPluginTest, CanInitializeAndShutdownPlugin) { + EXPECT_EQ(NPERR_NO_ERROR, + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS)); + EXPECT_EQ(NPERR_NO_ERROR, gpu_plugin::NP_Shutdown()); +} + +TEST_F(GPUPluginTest, InitializeFailsIfBrowserFuncsIsNull) { + EXPECT_EQ(NPERR_INVALID_FUNCTABLE_ERROR, + gpu_plugin::NP_Initialize(NULL INITIALIZE_PLUGIN_FUNCS)); +} + +TEST_F(GPUPluginTest, InitializeFailsIfAlreadyInitialized) { + EXPECT_EQ(NPERR_NO_ERROR, + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS)); + EXPECT_EQ(NPERR_GENERIC_ERROR, + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS)); + EXPECT_EQ(NPERR_NO_ERROR, gpu_plugin::NP_Shutdown()); +} + +TEST_F(GPUPluginTest, ShutdownFailsIfNotInitialized) { + EXPECT_EQ(NPERR_GENERIC_ERROR, gpu_plugin::NP_Shutdown()); +} + +TEST_F(GPUPluginTest, NewReturnsErrorForInvalidInstance) { + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_INVALID_INSTANCE_ERROR, plugin_funcs_.newp( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + NULL, 0, 0, NULL, NULL, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, GetValueReturnsErrorForInvalidInstance) { + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + int* result = NULL; + EXPECT_EQ(NPERR_INVALID_INSTANCE_ERROR, plugin_funcs_.getvalue( + NULL, NPPVjavaClass, &result)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, DestroyReturnsErrorForInvalidInstance) { + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_INVALID_INSTANCE_ERROR, plugin_funcs_.destroy(NULL, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, SetWindowReturnsErrorForInvalidInstance) { + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_INVALID_INSTANCE_ERROR, plugin_funcs_.setwindow(NULL, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, HandleEventReturnsFalseForInvalidInstance) { + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(0, plugin_funcs_.event(NULL, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, NewCreatesAPluginObjectAndInitializesIt) { + StrictMock<MockPluginObject> plugin_object; + + EXPECT_CALL(*plugin_object_factory_, CreatePluginObject( + &npp_, const_cast<NPMIMEType>(GPUPluginObject::kPluginType))) + .WillOnce(Return(&plugin_object)); + + NPObject scriptable_object; + + EXPECT_CALL(plugin_object, New( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + 0, NULL, NULL, NULL)) + .WillOnce(Return(NPERR_NO_ERROR)); + + EXPECT_CALL(plugin_object, GetScriptableNPObject()) + .WillOnce(Return(&scriptable_object)); + + EXPECT_CALL(plugin_object, Destroy(static_cast<NPSavedData**>(NULL))) + .WillOnce(Return(NPERR_NO_ERROR)); + + EXPECT_CALL(plugin_object, Release()); + + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.newp( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + &npp_, 0, 0, NULL, NULL, NULL)); + + NPObject* result; + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.getvalue( + &npp_, NPPVpluginScriptableNPObject, &result)); + EXPECT_EQ(&scriptable_object, result); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.destroy(&npp_, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, NewFailsIfPluginObjectFactoryFails) { + EXPECT_CALL(*plugin_object_factory_, CreatePluginObject( + &npp_, const_cast<NPMIMEType>(GPUPluginObject::kPluginType))) + .WillOnce(Return(static_cast<PluginObject*>(NULL))); + + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs_.newp( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + &npp_, 0, 0, NULL, NULL, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, SetWindowForwardsToPluginObject) { + StrictMock<MockPluginObject> plugin_object; + + EXPECT_CALL(*plugin_object_factory_, CreatePluginObject( + &npp_, const_cast<NPMIMEType>(GPUPluginObject::kPluginType))) + .WillOnce(Return(&plugin_object)); + + EXPECT_CALL(plugin_object, New( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + 0, NULL, NULL, NULL)) + .WillOnce(Return(NPERR_NO_ERROR)); + + NPWindow window = {0}; + + EXPECT_CALL(plugin_object, SetWindow(&window)) + .WillOnce(Return(NPERR_NO_ERROR)); + + EXPECT_CALL(plugin_object, Destroy(static_cast<NPSavedData**>(NULL))) + .WillOnce(Return(NPERR_NO_ERROR)); + + EXPECT_CALL(plugin_object, Release()); + + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.newp( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + &npp_, 0, 0, NULL, NULL, NULL)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.setwindow(&npp_, &window)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.destroy(&npp_, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, HandleEventForwardsToPluginObject) { + StrictMock<MockPluginObject> plugin_object; + + EXPECT_CALL(*plugin_object_factory_, CreatePluginObject( + &npp_, const_cast<NPMIMEType>(GPUPluginObject::kPluginType))) + .WillOnce(Return(&plugin_object)); + + EXPECT_CALL(plugin_object, New( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + 0, NULL, NULL, NULL)) + .WillOnce(Return(NPERR_NO_ERROR)); + + NPEvent event = {0}; + + EXPECT_CALL(plugin_object, HandleEvent(&event)) + .WillOnce(Return(7)); + + EXPECT_CALL(plugin_object, Destroy(static_cast<NPSavedData**>(NULL))) + .WillOnce(Return(NPERR_NO_ERROR)); + + EXPECT_CALL(plugin_object, Release()); + + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.newp( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + &npp_, 0, 0, NULL, NULL, NULL)); + + EXPECT_EQ(7, plugin_funcs_.event(&npp_, &event)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.destroy(&npp_, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +TEST_F(GPUPluginTest, GetValueReturnsErrorForUnknownVariable) { + StrictMock<MockPluginObject> plugin_object; + + EXPECT_CALL(*plugin_object_factory_, CreatePluginObject( + &npp_, const_cast<NPMIMEType>(GPUPluginObject::kPluginType))) + .WillOnce(Return(&plugin_object)); + + EXPECT_CALL(plugin_object, New( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + 0, NULL, NULL, NULL)) + .WillOnce(Return(NPERR_NO_ERROR)); + + EXPECT_CALL(plugin_object, Destroy(static_cast<NPSavedData**>(NULL))) + .WillOnce(Return(NPERR_NO_ERROR)); + + EXPECT_CALL(plugin_object, Release()); + + gpu_plugin::NP_GetEntryPoints(&plugin_funcs_); + gpu_plugin::NP_Initialize(&browser_funcs_ INITIALIZE_PLUGIN_FUNCS); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.newp( + const_cast<NPMIMEType>(GPUPluginObject::kPluginType), + &npp_, 0, 0, NULL, NULL, NULL)); + + int* result = NULL; + EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs_.getvalue( + &npp_, NPPVjavaClass, &result)); + + EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs_.destroy(&npp_, NULL)); + + gpu_plugin::NP_Shutdown(); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_processor.cc b/o3d/gpu/gpu_plugin/gpu_processor.cc new file mode 100644 index 0000000..7b97917 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_processor.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/gpu_processor.h" + +using ::base::SharedMemory; + +namespace gpu_plugin { + +GPUProcessor::~GPUProcessor() { +} + +namespace { +void InvokeProcessCommands(void* data) { + static_cast<GPUProcessor*>(data)->ProcessCommands(); +} +} // namespace anonymous + +void GPUProcessor::ProcessCommands() { + if (command_buffer_->GetErrorStatus()) + return; + + parser_->set_put(command_buffer_->GetPutOffset()); + + int commands_processed = 0; + while (commands_processed < commands_per_update_ && !parser_->IsEmpty()) { + command_buffer::parse_error::ParseError parse_error = + parser_->ProcessCommand(); + switch (parse_error) { + case command_buffer::parse_error::kParseUnknownCommand: + case command_buffer::parse_error::kParseInvalidArguments: + command_buffer_->SetParseError(parse_error); + break; + + case command_buffer::parse_error::kParseInvalidSize: + case command_buffer::parse_error::kParseOutOfBounds: + command_buffer_->SetParseError(parse_error); + command_buffer_->RaiseErrorStatus(); + return; + } + + ++commands_processed; + } + + command_buffer_->SetGetOffset(static_cast<int32>(parser_->get())); + + if (!parser_->IsEmpty()) { + NPBrowser::get()->PluginThreadAsyncCall(npp_, InvokeProcessCommands, this); + } +} + +void *GPUProcessor::GetSharedMemoryAddress(int32 shm_id) { + SharedMemory* shared_memory = command_buffer_->GetTransferBuffer(shm_id); + if (!shared_memory) + return NULL; + + if (!shared_memory->memory()) { + if (!shared_memory->Map(shared_memory->max_size())) + return NULL; + } + + return shared_memory->memory(); +} + +// TODO(apatrick): Consolidate this with the above and return both the address +// and size. +size_t GPUProcessor::GetSharedMemorySize(int32 shm_id) { + SharedMemory* shared_memory = command_buffer_->GetTransferBuffer(shm_id); + if (!shared_memory) + return 0; + + return shared_memory->max_size(); +} + +void GPUProcessor::set_token(int32 token) { + command_buffer_->SetToken(token); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/gpu_plugin/gpu_processor.h b/o3d/gpu/gpu_plugin/gpu_processor.h new file mode 100644 index 0000000..31617b0 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_processor.h @@ -0,0 +1,119 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_GPU_PLUGIN_GPU_PROCESSOR_H_ +#define GPU_GPU_PLUGIN_GPU_PROCESSOR_H_ + +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/shared_memory.h" +#include "gpu/command_buffer/service/cmd_buffer_engine.h" +#include "gpu/command_buffer/service/cmd_parser.h" +#include "gpu/command_buffer/service/gapi_decoder.h" +#include "gpu/gpu_plugin/command_buffer.h" +#include "gpu/np_utils/np_object_pointer.h" + +#if defined(CB_SERVICE_D3D9) +#include "gpu/command_buffer/service/gapi_d3d9.h" +#elif defined(CB_SERVICE_GL) +#include "gpu/command_buffer/service/gapi_gl.h" +#else +#error command buffer service not defined +#endif + +namespace gpu_plugin { + +// This class processes commands in a command buffer. It is event driven and +// posts tasks to the current message loop to do additional work. +class GPUProcessor : public ::base::RefCounted<GPUProcessor>, + public command_buffer::CommandBufferEngine { + public: +#if defined(CB_SERVICE_D3D9) + typedef command_buffer::o3d::GAPID3D9 GPUGAPIInterface; +#elif defined(CB_SERVICE_GL) + typedef command_buffer::o3d::GAPIGL GPUGAPIInterface; +#else +#error command buffer service not defined +#endif + + GPUProcessor(NPP npp, + CommandBuffer* command_buffer); + + // This constructor is for unit tests. + GPUProcessor(NPP npp, + CommandBuffer* command_buffer, + GPUGAPIInterface* gapi, + command_buffer::o3d::GAPIDecoder* decoder, + command_buffer::CommandParser* parser, + int commands_per_update); + + virtual bool Initialize(HWND hwnd); + + virtual ~GPUProcessor(); + + virtual void Destroy(); + + virtual void ProcessCommands(); + +#if defined(OS_WIN) + virtual bool SetWindow(HWND handle, int width, int height); +#endif + + // Implementation of CommandBufferEngine. + + // Gets the base address of a registered shared memory buffer. + // Parameters: + // shm_id: the identifier for the shared memory buffer. + virtual void *GetSharedMemoryAddress(int32 shm_id); + + // Gets the size of a registered shared memory buffer. + // Parameters: + // shm_id: the identifier for the shared memory buffer. + virtual size_t GetSharedMemorySize(int32 shm_id); + + // Sets the token value. + virtual void set_token(int32 token); + + private: + NPP npp_; + + // The GPUProcessor holds a weak reference to the CommandBuffer. The + // CommandBuffer owns the GPUProcessor and holds a strong reference to it + // through the ProcessCommands callback. + CommandBuffer* command_buffer_; + + scoped_ptr< ::base::SharedMemory> mapped_ring_buffer_; + int commands_per_update_; + + scoped_ptr<GPUGAPIInterface> gapi_; + scoped_ptr<command_buffer::o3d::GAPIDecoder> decoder_; + scoped_ptr<command_buffer::CommandParser> parser_; +}; + +} // namespace gpu_plugin + +// Callbacks to the GPUProcessor hold a reference count. +template <typename Method> +class CallbackStorage<gpu_plugin::GPUProcessor, Method> { + public: + CallbackStorage(gpu_plugin::GPUProcessor* obj, Method method) + : obj_(obj), + meth_(method) { + DCHECK(obj_); + obj_->AddRef(); + } + + ~CallbackStorage() { + obj_->Release(); + } + + protected: + gpu_plugin::GPUProcessor* obj_; + Method meth_; + + private: + DISALLOW_COPY_AND_ASSIGN(CallbackStorage); +}; + +#endif // GPU_GPU_PLUGIN_GPU_PROCESSOR_H_ diff --git a/o3d/gpu/gpu_plugin/gpu_processor_mock.h b/o3d/gpu/gpu_plugin/gpu_processor_mock.h new file mode 100644 index 0000000..e93c155 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_processor_mock.h @@ -0,0 +1,41 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_GPU_PLUGIN_GPU_PROCESSOR_MOCK_H_ +#define GPU_GPU_PLUGIN_GPU_PROCESSOR_MOCK_H_ + +#include "gpu/gpu_plugin/gpu_processor.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace gpu_plugin { + +class MockGPUProcessor : public GPUProcessor { + public: + MockGPUProcessor(NPP npp, + CommandBuffer* command_buffer) + : GPUProcessor(npp, command_buffer) { + } + +#if defined(OS_WIN) + MOCK_METHOD1(Initialize, bool(HWND handle)); +#endif + + MOCK_METHOD0(Destroy, void()); + MOCK_METHOD0(ProcessCommands, void()); + +#if defined(OS_WIN) + MOCK_METHOD3(SetWindow, bool(HWND handle, int width, int height)); +#endif + + MOCK_METHOD1(GetSharedMemoryAddress, void*(int32 shm_id)); + MOCK_METHOD1(GetSharedMemorySize, size_t(int32 shm_id)); + MOCK_METHOD1(set_token, void(int32 token)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockGPUProcessor); +}; + +} // namespace gpu_plugin + +#endif // GPU_GPU_PLUGIN_GPU_PROCESSOR_MOCK_H_ diff --git a/o3d/gpu/gpu_plugin/gpu_processor_unittest.cc b/o3d/gpu/gpu_plugin/gpu_processor_unittest.cc new file mode 100644 index 0000000..97c44b9 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_processor_unittest.cc @@ -0,0 +1,309 @@ +// Copyright (c) 2006-2008 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 "base/at_exit.h" +#include "base/message_loop.h" +#include "gpu/command_buffer/service/mocks.h" +#include "gpu/gpu_plugin/command_buffer_mock.h" +#include "gpu/gpu_plugin/gpu_processor.h" +#include "gpu/np_utils/np_browser_mock.h" +#include "gpu/np_utils/np_object_pointer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +using ::base::SharedMemory; + +using testing::_; +using testing::DoAll; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::SetArgumentPointee; +using testing::StrictMock; + +namespace gpu_plugin { + +const size_t kRingBufferSize = 1024; +const size_t kRingBufferEntries = kRingBufferSize / sizeof(int32); + +class GPUProcessorTest : public testing::Test { + protected: + virtual void SetUp() { + shared_memory_.reset(new SharedMemory); + shared_memory_->Create(std::wstring(), false, false, kRingBufferSize); + shared_memory_->Map(kRingBufferSize); + buffer_ = static_cast<int32*>(shared_memory_->memory()); + + memset(buffer_, 0, kRingBufferSize); + + // Don't mock PluginThreadAsyncCall. Have it schedule the task. + ON_CALL(mock_browser_, PluginThreadAsyncCall(_, _, _)) + .WillByDefault(Invoke(&mock_browser_, + &MockNPBrowser::ConcretePluginThreadAsyncCall)); + + command_buffer_ = NPCreateObject<MockCommandBuffer>(NULL); + ON_CALL(*command_buffer_.Get(), GetRingBuffer()) + .WillByDefault(Return(shared_memory_.get())); + ON_CALL(*command_buffer_.Get(), GetSize()) + .WillByDefault(Return(kRingBufferEntries)); + +#if defined(OS_WIN) + gapi_ = new GPUProcessor::GPUGAPIInterface; +#endif + + async_api_.reset(new StrictMock<command_buffer::AsyncAPIMock>); + + decoder_ = new command_buffer::o3d::GAPIDecoder(gapi_); + + parser_ = new command_buffer::CommandParser(buffer_, + kRingBufferEntries, + 0, + kRingBufferEntries, + 0, + async_api_.get()); + + processor_ = new GPUProcessor(NULL, + command_buffer_.Get(), + gapi_, + decoder_, + parser_, + 2); + } + + virtual void TearDown() { + // Ensure that any unexpected tasks posted by the GPU processor are executed + // in order to fail the test. + MessageLoop::current()->RunAllPending(); + } + + base::AtExitManager at_exit_manager; + MessageLoop message_loop; + MockNPBrowser mock_browser_; + NPObjectPointer<MockCommandBuffer> command_buffer_; + scoped_ptr<SharedMemory> shared_memory_; + int32* buffer_; + command_buffer::o3d::GAPIDecoder* decoder_; + command_buffer::CommandParser* parser_; + scoped_ptr<command_buffer::AsyncAPIMock> async_api_; + scoped_refptr<GPUProcessor> processor_; + +#if defined(OS_WIN) + GPUProcessor::GPUGAPIInterface* gapi_; +#endif +}; + +TEST_F(GPUProcessorTest, ProcessorDoesNothingIfRingBufferIsEmpty) { + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(0)); + EXPECT_CALL(*command_buffer_, SetGetOffset(0)); + + processor_->ProcessCommands(); + + EXPECT_EQ(command_buffer::parse_error::kParseNoError, + command_buffer_->ResetParseError()); + EXPECT_FALSE(command_buffer_->GetErrorStatus()); +} + +TEST_F(GPUProcessorTest, ProcessesOneCommand) { + command_buffer::CommandHeader* header = + reinterpret_cast<command_buffer::CommandHeader*>(&buffer_[0]); + header[0].command = 7; + header[0].size = 2; + buffer_[1] = 123; + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(2)); + EXPECT_CALL(*command_buffer_, SetGetOffset(2)); + + EXPECT_CALL(*async_api_, DoCommand(7, 1, &buffer_[0])) + .WillOnce(Return(command_buffer::parse_error::kParseNoError)); + + processor_->ProcessCommands(); + + EXPECT_EQ(command_buffer::parse_error::kParseNoError, + command_buffer_->ResetParseError()); + EXPECT_FALSE(command_buffer_->GetErrorStatus()); +} + +TEST_F(GPUProcessorTest, ProcessesTwoCommands) { + command_buffer::CommandHeader* header = + reinterpret_cast<command_buffer::CommandHeader*>(&buffer_[0]); + header[0].command = 7; + header[0].size = 2; + buffer_[1] = 123; + header[2].command = 8; + header[2].size = 1; + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(3)); + EXPECT_CALL(*command_buffer_, SetGetOffset(3)); + + EXPECT_CALL(*async_api_, DoCommand(7, 1, &buffer_[0])) + .WillOnce(Return(command_buffer::parse_error::kParseNoError)); + + EXPECT_CALL(*async_api_, DoCommand(8, 0, &buffer_[2])) + .WillOnce(Return(command_buffer::parse_error::kParseNoError)); + + processor_->ProcessCommands(); +} + +TEST_F(GPUProcessorTest, PostsTaskToFinishRemainingCommands) { + command_buffer::CommandHeader* header = + reinterpret_cast<command_buffer::CommandHeader*>(&buffer_[0]); + header[0].command = 7; + header[0].size = 2; + buffer_[1] = 123; + header[2].command = 8; + header[2].size = 1; + header[3].command = 9; + header[3].size = 1; + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(4)); + + EXPECT_CALL(*async_api_, DoCommand(7, 1, &buffer_[0])) + .WillOnce(Return(command_buffer::parse_error::kParseNoError)); + + EXPECT_CALL(*async_api_, DoCommand(8, 0, &buffer_[2])) + .WillOnce(Return(command_buffer::parse_error::kParseNoError)); + + EXPECT_CALL(*command_buffer_, SetGetOffset(3)); + + processor_->ProcessCommands(); + + // ProcessCommands is called a second time when the pending task is run. + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(4)); + + EXPECT_CALL(*async_api_, DoCommand(9, 0, &buffer_[3])) + .WillOnce(Return(command_buffer::parse_error::kParseNoError)); + + EXPECT_CALL(*command_buffer_, SetGetOffset(4)); + + MessageLoop::current()->RunAllPending(); +} + +TEST_F(GPUProcessorTest, SetsErrorCodeOnCommandBuffer) { + command_buffer::CommandHeader* header = + reinterpret_cast<command_buffer::CommandHeader*>(&buffer_[0]); + header[0].command = 7; + header[0].size = 1; + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(1)); + EXPECT_CALL(*command_buffer_, SetGetOffset(1)); + + EXPECT_CALL(*async_api_, DoCommand(7, 0, &buffer_[0])) + .WillOnce(Return( + command_buffer::parse_error::kParseUnknownCommand)); + + processor_->ProcessCommands(); + + EXPECT_EQ(command_buffer::parse_error::kParseUnknownCommand, + command_buffer_->ResetParseError()); + EXPECT_FALSE(command_buffer_->GetErrorStatus()); +} + +TEST_F(GPUProcessorTest, + RecoverableParseErrorsAreNotClearedByFollowingSuccessfulCommands) { + command_buffer::CommandHeader* header = + reinterpret_cast<command_buffer::CommandHeader*>(&buffer_[0]); + header[0].command = 7; + header[0].size = 1; + header[1].command = 8; + header[1].size = 1; + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(2)); + EXPECT_CALL(*command_buffer_, SetGetOffset(2)); + + EXPECT_CALL(*async_api_, DoCommand(7, 0, &buffer_[0])) + .WillOnce(Return( + command_buffer::parse_error::kParseUnknownCommand)); + + EXPECT_CALL(*async_api_, DoCommand(8, 0, &buffer_[1])) + .WillOnce(Return(command_buffer::parse_error::kParseNoError)); + + processor_->ProcessCommands(); + + EXPECT_EQ(command_buffer::parse_error::kParseUnknownCommand, + command_buffer_->ResetParseError()); + EXPECT_FALSE(command_buffer_->GetErrorStatus()); +} + +TEST_F(GPUProcessorTest, UnrecoverableParseErrorsRaiseTheErrorStatus) { + command_buffer::CommandHeader* header = + reinterpret_cast<command_buffer::CommandHeader*>(&buffer_[0]); + header[0].command = 7; + header[0].size = 1; + header[1].command = 8; + header[1].size = 1; + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(2)); + + EXPECT_CALL(*async_api_, DoCommand(7, 0, &buffer_[0])) + .WillOnce(Return(command_buffer::parse_error::kParseInvalidSize)); + + processor_->ProcessCommands(); + + EXPECT_EQ(command_buffer::parse_error::kParseInvalidSize, + command_buffer_->ResetParseError()); + EXPECT_TRUE(command_buffer_->GetErrorStatus()); +} + +TEST_F(GPUProcessorTest, ProcessCommandsDoesNothingAfterUnrecoverableError) { + command_buffer::CommandHeader* header = + reinterpret_cast<command_buffer::CommandHeader*>(&buffer_[0]); + header[0].command = 7; + header[0].size = 1; + header[1].command = 8; + header[1].size = 1; + + EXPECT_CALL(*command_buffer_, GetPutOffset()) + .WillOnce(Return(2)); + + EXPECT_CALL(*async_api_, DoCommand(7, 0, &buffer_[0])) + .WillOnce(Return(command_buffer::parse_error::kParseInvalidSize)); + + processor_->ProcessCommands(); + processor_->ProcessCommands(); + + EXPECT_EQ(command_buffer::parse_error::kParseInvalidSize, + command_buffer_->ResetParseError()); + EXPECT_TRUE(command_buffer_->GetErrorStatus()); +} + +TEST_F(GPUProcessorTest, CanGetAddressOfSharedMemory) { + EXPECT_CALL(*command_buffer_.Get(), GetTransferBuffer(7)) + .WillOnce(Return(shared_memory_.get())); + + EXPECT_EQ(&buffer_[0], processor_->GetSharedMemoryAddress(7)); +} + +ACTION_P2(SetPointee, address, value) { + *address = value; +} + +TEST_F(GPUProcessorTest, GetAddressOfSharedMemoryMapsMemoryIfUnmapped) { + EXPECT_CALL(*command_buffer_.Get(), GetTransferBuffer(7)) + .WillOnce(Return(shared_memory_.get())); + + EXPECT_EQ(&buffer_[0], processor_->GetSharedMemoryAddress(7)); +} + +TEST_F(GPUProcessorTest, CanGetSizeOfSharedMemory) { + EXPECT_CALL(*command_buffer_.Get(), GetTransferBuffer(7)) + .WillOnce(Return(shared_memory_.get())); + + EXPECT_EQ(kRingBufferSize, processor_->GetSharedMemorySize(7)); +} + +TEST_F(GPUProcessorTest, SetTokenForwardsToCommandBuffer) { + processor_->set_token(7); + EXPECT_EQ(7, command_buffer_->GetToken()); +} + +} // namespace gpu_plugin
\ No newline at end of file diff --git a/o3d/gpu/gpu_plugin/gpu_processor_win.cc b/o3d/gpu/gpu_plugin/gpu_processor_win.cc new file mode 100644 index 0000000..4c62098 --- /dev/null +++ b/o3d/gpu/gpu_plugin/gpu_processor_win.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2006-2008 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 <windows.h> + +#include "gpu/gpu_plugin/gpu_processor.h" + +using ::base::SharedMemory; + +namespace gpu_plugin { + +GPUProcessor::GPUProcessor(NPP npp, + CommandBuffer* command_buffer) + : npp_(npp), + command_buffer_(command_buffer), + commands_per_update_(100) { + DCHECK(command_buffer); + gapi_.reset(new GPUGAPIInterface); + decoder_.reset(new command_buffer::o3d::GAPIDecoder(gapi_.get())); + decoder_->set_engine(this); +} + +GPUProcessor::GPUProcessor(NPP npp, + CommandBuffer* command_buffer, + GPUGAPIInterface* gapi, + command_buffer::o3d::GAPIDecoder* decoder, + command_buffer::CommandParser* parser, + int commands_per_update) + : npp_(npp), + command_buffer_(command_buffer), + commands_per_update_(commands_per_update) { + DCHECK(command_buffer); + gapi_.reset(gapi); + decoder_.reset(decoder); + parser_.reset(parser); +} + +bool GPUProcessor::Initialize(HWND handle) { + DCHECK(handle); + + // Cannot reinitialize. + if (gapi_->hwnd() != NULL) + return false; + + // Map the ring buffer and create the parser. + SharedMemory* ring_buffer = command_buffer_->GetRingBuffer(); + if (ring_buffer) { + size_t size = ring_buffer->max_size(); + if (!ring_buffer->Map(size)) { + return false; + } + + void* ptr = ring_buffer->memory(); + parser_.reset(new command_buffer::CommandParser(ptr, size, 0, size, 0, + decoder_.get())); + } else { + parser_.reset(new command_buffer::CommandParser(NULL, 0, 0, 0, 0, + decoder_.get())); + } + + // Initialize GAPI immediately if the window handle is valid. + gapi_->set_hwnd(handle); + return gapi_->Initialize(); +} + +void GPUProcessor::Destroy() { + // Destroy GAPI if window handle has not already become invalid. + if (gapi_->hwnd()) { + gapi_->Destroy(); + gapi_->set_hwnd(NULL); + } +} + +bool GPUProcessor::SetWindow(HWND handle, int width, int height) { + if (handle == NULL) { + // Destroy GAPI when the window handle becomes invalid. + Destroy(); + return true; + } else { + return Initialize(handle); + } +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/default_np_object.h b/o3d/gpu/np_utils/default_np_object.h new file mode 100644 index 0000000..575dabc --- /dev/null +++ b/o3d/gpu/np_utils/default_np_object.h @@ -0,0 +1,84 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_DEFAULT_NP_OBJECT_H_ +#define GPU_NP_UTILS_DEFAULT_NP_OBJECT_H_ + +#include "base/basictypes.h" +#include "gpu/np_utils/np_headers.h" + +namespace gpu_plugin { + +class BaseNPDispatcher; + +// This class implements each of the functions in the NPClass interface. They +// all return error by default. Note that these are not virtual functions and +// this is not an interface. This class can be used as a mixin so that an +// NPObject class does not need to implement every NPClass function but rather +// inherits a default from DefaultNPObject. +template <typename RootClass> +class DefaultNPObject : public RootClass { + public: + void Invalidate() {} + + bool HasMethod(NPIdentifier name) { + return false; + } + + bool Invoke(NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return false; + } + + bool InvokeDefault(const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return false; + } + + bool HasProperty(NPIdentifier name) { + return false; + } + + bool GetProperty(NPIdentifier name, NPVariant* result) { + return false; + } + + bool SetProperty(NPIdentifier name, const NPVariant* value) { + return false; + } + + bool RemoveProperty(NPIdentifier name) { + return false; + } + + bool Enumerate(NPIdentifier** names, + uint32_t* count) { + *names = NULL; + *count = 0; + return true; + } + + bool Construct(const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return false; + } + + static BaseNPDispatcher* GetDispatcherChain() { + return NULL; + } + + protected: + DefaultNPObject() {} + virtual ~DefaultNPObject() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DefaultNPObject); +}; +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_DEFAULT_NP_OBJECT_H_ diff --git a/o3d/gpu/np_utils/dispatched_np_object_unittest.cc b/o3d/gpu/np_utils/dispatched_np_object_unittest.cc new file mode 100644 index 0000000..d4b2f96 --- /dev/null +++ b/o3d/gpu/np_utils/dispatched_np_object_unittest.cc @@ -0,0 +1,403 @@ +// Copyright (c) 2006-2008 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 <string> + +#include "gpu/np_utils/default_np_object.h" +#include "gpu/np_utils/np_browser_stub.h" +#include "gpu/np_utils/np_dispatcher.h" +#include "gpu/np_utils/np_object_mock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Return; +using testing::StrictMock; + +namespace gpu_plugin { + +// This mock class has a dispatcher chain with an entry for each mocked +// function. The tests that follow that invoking an NPAPI method calls the +// corresponding mocked member function. +class MockDispatchedNPObject : public DefaultNPObject<NPObject> { + public: + explicit MockDispatchedNPObject(NPP npp) { + } + + MOCK_METHOD0(VoidReturnNoParams, void()); + MOCK_METHOD1(VoidReturnBoolParam, void(bool)); + MOCK_METHOD1(VoidReturnIntParam, void(int)); + MOCK_METHOD1(VoidReturnFloatParam, void(float)); + MOCK_METHOD1(VoidReturnDoubleParam, void(double)); + MOCK_METHOD1(VoidReturnStringParam, void(std::string)); + MOCK_METHOD1(VoidReturnObjectParam, void(NPObjectPointer<NPObject>)); + MOCK_METHOD2(VoidReturnTwoParams, void(bool, int)); + MOCK_METHOD0(Overloaded, void()); + MOCK_METHOD1(Overloaded, void(bool)); + MOCK_METHOD1(Overloaded, void(std::string)); + MOCK_METHOD0(BoolReturn, bool()); + MOCK_METHOD0(IntReturn, int()); + MOCK_METHOD0(FloatReturn, float()); + MOCK_METHOD0(DoubleReturn, double()); + MOCK_METHOD0(StringReturn, std::string()); + MOCK_METHOD0(ObjectReturn, NPObjectPointer<NPObject>()); + + NP_UTILS_BEGIN_DISPATCHER_CHAIN(MockDispatchedNPObject, DefaultNPObject<NPObject>) + NP_UTILS_DISPATCHER(VoidReturnNoParams, void()) + NP_UTILS_DISPATCHER(VoidReturnBoolParam, void(bool)) + NP_UTILS_DISPATCHER(VoidReturnIntParam, void(int)) + NP_UTILS_DISPATCHER(VoidReturnFloatParam, void(float)) + NP_UTILS_DISPATCHER(VoidReturnDoubleParam, void(double)) + NP_UTILS_DISPATCHER(VoidReturnStringParam, void(std::string)) + NP_UTILS_DISPATCHER(VoidReturnObjectParam, void(NPObjectPointer<NPObject>)) + NP_UTILS_DISPATCHER(VoidReturnTwoParams, void(bool, int)) + NP_UTILS_DISPATCHER(Overloaded, void()) + NP_UTILS_DISPATCHER(Overloaded, void(bool)) + NP_UTILS_DISPATCHER(Overloaded, void(std::string)) + NP_UTILS_DISPATCHER(BoolReturn, bool()) + NP_UTILS_DISPATCHER(IntReturn, int()) + NP_UTILS_DISPATCHER(FloatReturn, float()) + NP_UTILS_DISPATCHER(DoubleReturn, double()) + NP_UTILS_DISPATCHER(StringReturn, std::string()) + NP_UTILS_DISPATCHER(ObjectReturn, NPObjectPointer<NPObject>()); + NP_UTILS_END_DISPATCHER_CHAIN +}; + +class DispatchedNPObjectTest : public testing::Test { + protected: + virtual void SetUp() { + object_ = NPCreateObject<StrictMock<MockDispatchedNPObject> >(NULL); + passed_object_ = NPCreateObject<MockNPObject>(NULL); + + for (int i = 0; i != arraysize(args_); ++i) { + NULL_TO_NPVARIANT(args_[i]); + } + NULL_TO_NPVARIANT(result_); + } + + StubNPBrowser stub_browser_; + NPVariant args_[3]; + NPVariant result_; + NPObjectPointer<MockDispatchedNPObject> object_; + NPObjectPointer<NPObject> passed_object_; +}; + +TEST_F(DispatchedNPObjectTest, CannotInvokeMissingFunction) { + EXPECT_FALSE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("missing"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnNoParams) { + EXPECT_CALL(*object_, VoidReturnNoParams()); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnNoParams"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, + CannotInvokeVoidReturnNoParamsWithTooManyParams) { + EXPECT_FALSE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnNoParams"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnIntParam) { + EXPECT_CALL(*object_, VoidReturnIntParam(7)); + + INT32_TO_NPVARIANT(7, args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnIntParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnBoolParam) { + EXPECT_CALL(*object_, VoidReturnBoolParam(true)); + + BOOLEAN_TO_NPVARIANT(true, args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnBoolParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnFloatParamWithDoubleParam) { + EXPECT_CALL(*object_, VoidReturnFloatParam(7.0f)); + + DOUBLE_TO_NPVARIANT(7.0, args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnFloatParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnFloatParamWithIntParam) { + EXPECT_CALL(*object_, VoidReturnFloatParam(7.0f)); + + INT32_TO_NPVARIANT(7, args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnFloatParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnDoubleParamWithDoubleParam) { + EXPECT_CALL(*object_, VoidReturnDoubleParam(7.0)); + + DOUBLE_TO_NPVARIANT(7.0, args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnDoubleParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnDoubleParamWithIntParam) { + EXPECT_CALL(*object_, VoidReturnDoubleParam(7.0f)); + + INT32_TO_NPVARIANT(7, args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnDoubleParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnStringParam) { + EXPECT_CALL(*object_, VoidReturnStringParam(std::string("hello"))); + + STRINGZ_TO_NPVARIANT("hello", args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnStringParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnObjectParamWithObject) { + EXPECT_CALL(*object_, VoidReturnObjectParam(passed_object_)); + + OBJECT_TO_NPVARIANT(passed_object_.Get(), args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnObjectParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnObjectParamWithNull) { + EXPECT_CALL( + *object_, + VoidReturnObjectParam(NPObjectPointer<NPObject>())); + + NULL_TO_NPVARIANT(args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnObjectParam"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeVoidReturnTwoParams) { + EXPECT_CALL(*object_, VoidReturnTwoParams(false, 7)); + + BOOLEAN_TO_NPVARIANT(false, args_[0]); + INT32_TO_NPVARIANT(7, args_[1]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("voidReturnTwoParams"), + args_, + 2, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeOverloadedWithNoParams) { + EXPECT_CALL(*object_, Overloaded()); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("overloaded"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeOverloadedWithOneStringParam) { + EXPECT_CALL(*object_, Overloaded(std::string("hello"))); + + STRINGZ_TO_NPVARIANT("hello", args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("overloaded"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeOverloadedWithOneBoolParam) { + EXPECT_CALL(*object_, Overloaded(true)); + + BOOLEAN_TO_NPVARIANT(true, args_[0]); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("overloaded"), + args_, + 1, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_VOID(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeBoolReturn) { + EXPECT_CALL(*object_, BoolReturn()).WillOnce(Return(true)); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("boolReturn"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(result_)); + EXPECT_TRUE(NPVARIANT_TO_BOOLEAN(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeIntReturn) { + EXPECT_CALL(*object_, IntReturn()).WillOnce(Return(7)); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("intReturn"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_INT32(result_)); + EXPECT_EQ(7, NPVARIANT_TO_INT32(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeFloatReturn) { + EXPECT_CALL(*object_, FloatReturn()).WillOnce(Return(7.0f)); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("floatReturn"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_DOUBLE(result_)); + EXPECT_EQ(7.0, NPVARIANT_TO_DOUBLE(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeDoubleReturn) { + EXPECT_CALL(*object_, DoubleReturn()).WillOnce(Return(7.0)); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("doubleReturn"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_DOUBLE(result_)); + EXPECT_EQ(7.0, NPVARIANT_TO_DOUBLE(result_)); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeStringReturn) { + EXPECT_CALL(*object_, StringReturn()).WillOnce(Return(std::string("hello"))); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("stringReturn"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_STRING(result_)); + + NPString& str = NPVARIANT_TO_STRING(result_); + EXPECT_EQ(std::string("hello"), + std::string(str.UTF8Characters, str.UTF8Length)); + + // Callee is responsible for releasing string. + NPBrowser::get()->ReleaseVariantValue(&result_); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeObjectReturnWithObject) { + EXPECT_CALL(*object_, ObjectReturn()).WillOnce(Return(passed_object_)); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("objectReturn"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_OBJECT(result_)); + EXPECT_EQ(passed_object_.Get(), NPVARIANT_TO_OBJECT(result_)); + + NPBrowser::get()->ReleaseVariantValue(&result_); +} + +TEST_F(DispatchedNPObjectTest, CanInvokeObjectReturnWithNull) { + EXPECT_CALL(*object_, ObjectReturn()) + .WillOnce(Return(NPObjectPointer<NPObject>())); + + EXPECT_TRUE(object_->Invoke( + NPBrowser::get()->GetStringIdentifier("objectReturn"), + NULL, + 0, + &result_)); + EXPECT_TRUE(NPVARIANT_IS_NULL(result_)); +} + +TEST_F(DispatchedNPObjectTest, HasMethodReturnsTrueIfMatchingMemberVariable) { + EXPECT_TRUE(object_->HasMethod( + NPBrowser::get()->GetStringIdentifier("objectReturn"))); +} + +TEST_F(DispatchedNPObjectTest, HasMethodReturnsTrueIfNoMatchingMemberVariable) { + EXPECT_FALSE(object_->HasMethod( + NPBrowser::get()->GetStringIdentifier("missing"))); +} + +TEST_F(DispatchedNPObjectTest, EnumeratesAllAvailableMethods) { + NPIdentifier* names; + uint32_t num_names; + ASSERT_TRUE(object_->Enumerate(&names, &num_names)); + + // Don't compare all of them; this test would need to change every time new + // dispatchers were added to the test NPObject class. Just compare the first + // registered (last in the dispatcher chain) and that more than one is + // returned. + EXPECT_GT(num_names, 1u); + EXPECT_EQ(NPBrowser::get()->GetStringIdentifier("voidReturnNoParams"), + names[num_names - 1]); + + NPBrowser::get()->MemFree(names); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/dynamic_np_object.cc b/o3d/gpu/np_utils/dynamic_np_object.cc new file mode 100644 index 0000000..153c189 --- /dev/null +++ b/o3d/gpu/np_utils/dynamic_np_object.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/dynamic_np_object.h" + +namespace gpu_plugin { + +DynamicNPObject::DynamicNPObject(NPP npp) { +} + +void DynamicNPObject::Invalidate() { + for (PropertyMap::iterator it = properties_.begin(); + it != properties_.end(); + ++it) { + it->second.Invalidate(); + } +} + +bool DynamicNPObject::HasProperty(NPIdentifier name) { + PropertyMap::iterator it = properties_.find(name); + return it != properties_.end(); +} + +bool DynamicNPObject::GetProperty(NPIdentifier name, NPVariant* result) { + PropertyMap::iterator it = properties_.find(name); + if (it == properties_.end()) + return false; + + it->second.CopyTo(result); + return true; +} + +bool DynamicNPObject::SetProperty(NPIdentifier name, const NPVariant* value) { + properties_[name] = *value; + return true; +} + +bool DynamicNPObject::RemoveProperty(NPIdentifier name) { + properties_.erase(name); + return false; +} + +bool DynamicNPObject::Enumerate(NPIdentifier** names, uint32_t* count) { + *names = static_cast<NPIdentifier*>( + NPBrowser::get()->MemAlloc(properties_.size() * sizeof(*names))); + *count = properties_.size(); + + int i = 0; + for (PropertyMap::iterator it = properties_.begin(); + it != properties_.end(); + ++it) { + (*names)[i] = it->first; + ++i; + } + + return true; +} +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/dynamic_np_object.h b/o3d/gpu/np_utils/dynamic_np_object.h new file mode 100644 index 0000000..8dd4892 --- /dev/null +++ b/o3d/gpu/np_utils/dynamic_np_object.h @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_DYNAMIC_NP_OBJECT_H_ +#define GPU_NP_UTILS_DYNAMIC_NP_OBJECT_H_ + +#include <map> + +#include "gpu/np_utils/default_np_object.h" +#include "gpu/np_utils/np_utils.h" + +namespace gpu_plugin { + +// NPObjects of this type have a dictionary of property name / variant pairs +// that can be changed at runtime through NPAPI. +class DynamicNPObject : public DefaultNPObject<NPObject> { + public: + explicit DynamicNPObject(NPP npp); + + void Invalidate(); + bool HasProperty(NPIdentifier name); + bool GetProperty(NPIdentifier name, NPVariant* result); + bool SetProperty(NPIdentifier name, const NPVariant* value); + bool RemoveProperty(NPIdentifier name); + bool Enumerate(NPIdentifier** names, uint32_t* count); + + private: + typedef std::map<NPIdentifier, SmartNPVariant> PropertyMap; + PropertyMap properties_; + DISALLOW_COPY_AND_ASSIGN(DynamicNPObject); +}; +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_DYNAMIC_NP_OBJECT_H_ diff --git a/o3d/gpu/np_utils/dynamic_np_object_unittest.cc b/o3d/gpu/np_utils/dynamic_np_object_unittest.cc new file mode 100644 index 0000000..56fcff7 --- /dev/null +++ b/o3d/gpu/np_utils/dynamic_np_object_unittest.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2006-2008 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 <string> + +#include "gpu/np_utils/dynamic_np_object.h" +#include "gpu/np_utils/np_browser_stub.h" +#include "gpu/np_utils/np_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Return; +using testing::StrictMock; + +namespace gpu_plugin { + +class NPDynamicNPObjectTest : public testing::Test { + protected: + virtual void SetUp() { + object_ = NPCreateObject<DynamicNPObject>(NULL); + } + + StubNPBrowser stub_browser_; + NPObjectPointer<DynamicNPObject> object_; +}; + +TEST_F(NPDynamicNPObjectTest, HasPropertyReturnsFalseForMissingProperty) { + EXPECT_FALSE(NPHasProperty(NULL, object_, "missing")); +} + +TEST_F(NPDynamicNPObjectTest, GetPropertyReturnsFalseForMissingProperty) { + int32 r; + EXPECT_FALSE(NPGetProperty(NULL, object_, "missing", &r)); +} + +TEST_F(NPDynamicNPObjectTest, CanSetProperty) { + EXPECT_TRUE(NPSetProperty(NULL, object_, "foo", 7)); + int32 r; + EXPECT_TRUE(NPHasProperty(NULL, object_, "foo")); + EXPECT_TRUE(NPGetProperty(NULL, object_, "foo", &r)); + EXPECT_EQ(7, r); +} + +TEST_F(NPDynamicNPObjectTest, CanRemoveProperty) { + EXPECT_TRUE(NPSetProperty(NULL, object_, "foo", 7)); + EXPECT_TRUE(NPHasProperty(NULL, object_, "foo")); + EXPECT_FALSE(NPRemoveProperty(NULL, object_, "foo")); + EXPECT_FALSE(NPHasProperty(NULL, object_, "foo")); + int32 r; + EXPECT_FALSE(NPGetProperty(NULL, object_, "foo", &r)); +} + +TEST_F(NPDynamicNPObjectTest, CanEnumerateProperties) { + EXPECT_TRUE(NPSetProperty(NULL, object_, "foo", 7)); + + NPIdentifier* names; + uint32 num_names; + EXPECT_TRUE(object_->_class->enumerate(object_.Get(), &names, &num_names)); + + EXPECT_EQ(1, num_names); + EXPECT_EQ(NPBrowser::get()->GetStringIdentifier("foo"), names[0]); + + NPBrowser::get()->MemFree(names); +} + +// Properties should not be +TEST_F(NPDynamicNPObjectTest, InvalidateNullsObjectProperties) { + EXPECT_EQ(1, object_->referenceCount); + { + EXPECT_TRUE(NPSetProperty(NULL, object_, "foo", object_)); + EXPECT_TRUE(NPHasProperty(NULL, object_, "foo")); + object_->_class->invalidate(object_.Get()); + EXPECT_TRUE(NPHasProperty(NULL, object_, "foo")); + NPObjectPointer<DynamicNPObject> r; + EXPECT_TRUE(NPGetProperty(NULL, object_, "foo", &r)); + EXPECT_TRUE(NULL == r.Get()); + } + // Invalidate did not release object + EXPECT_EQ(2, object_->referenceCount); + NPBrowser::get()->ReleaseObject(object_.Get()); +} +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_browser.cc b/o3d/gpu/np_utils/np_browser.cc new file mode 100644 index 0000000..3754d3b --- /dev/null +++ b/o3d/gpu/np_utils/np_browser.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/np_browser.h" +#include "base/logging.h" + +#if defined(O3D_IN_CHROME) +#include "webkit/glue/plugins/nphostapi.h" +#else +#include "o3d/third_party/npapi/include/npfunctions.h" +#endif + +namespace gpu_plugin { + +NPBrowser* NPBrowser::browser_; + +NPBrowser::NPBrowser(NPNetscapeFuncs* funcs) + : netscape_funcs_(funcs) { + // Make this the first browser in the linked list. + previous_browser_ = browser_; + browser_ = this; +} + +NPBrowser::~NPBrowser() { + // Remove this browser from the linked list. + DCHECK(browser_ == this); + browser_ = previous_browser_; +} + +NPIdentifier NPBrowser::GetStringIdentifier(const NPUTF8* name) { + return netscape_funcs_->getstringidentifier(name); +} + +void* NPBrowser::MemAlloc(size_t size) { + return netscape_funcs_->memalloc(size); +} + +void NPBrowser::MemFree(void* p) { + netscape_funcs_->memfree(p); +} + +NPObject* NPBrowser::CreateObject(NPP npp, const NPClass* cl) { + return netscape_funcs_->createobject(npp, const_cast<NPClass*>(cl)); +} + +NPObject* NPBrowser::RetainObject(NPObject* object) { + return netscape_funcs_->retainobject(object); +} + +void NPBrowser::ReleaseObject(NPObject* object) { + netscape_funcs_->releaseobject(object); +} + +void NPBrowser::ReleaseVariantValue(NPVariant* variant) { + netscape_funcs_->releasevariantvalue(variant); +} + +bool NPBrowser::HasProperty(NPP npp, + NPObject* object, + NPIdentifier name) { + return netscape_funcs_->hasproperty(npp, object, name); +} + +bool NPBrowser::GetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + NPVariant* result) { + return netscape_funcs_->getproperty(npp, object, name, result); +} + +bool NPBrowser::SetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* result) { + return netscape_funcs_->setproperty(npp, object, name, result); +} + +bool NPBrowser::RemoveProperty(NPP npp, + NPObject* object, + NPIdentifier name) { + return netscape_funcs_->removeproperty(npp, object, name); +} + +bool NPBrowser::HasMethod(NPP npp, + NPObject* object, + NPIdentifier name) { + return netscape_funcs_->hasmethod(npp, object, name); +} + +bool NPBrowser::Invoke(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return netscape_funcs_->invoke(npp, object, name, args, num_args, result); +} + +NPObject* NPBrowser::GetWindowNPObject(NPP npp) { + NPObject* window; + if (NPERR_NO_ERROR == netscape_funcs_->getvalue(npp, + NPNVWindowNPObject, + &window)) { + return window; + } else { + return NULL; + } +} + +void NPBrowser::PluginThreadAsyncCall(NPP npp, + PluginThreadAsyncCallProc callback, + void* data) { + netscape_funcs_->pluginthreadasynccall(npp, callback, data); +} + +uint32 NPBrowser::ScheduleTimer(NPP npp, + uint32 interval, + bool repeat, + TimerProc callback) { + return netscape_funcs_->scheduletimer(npp, interval, repeat, callback); +} + +void NPBrowser::UnscheduleTimer(NPP npp, uint32 timer_id) { + netscape_funcs_->unscheduletimer(npp, timer_id); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_browser.h b/o3d/gpu/np_utils/np_browser.h new file mode 100644 index 0000000..d792316 --- /dev/null +++ b/o3d/gpu/np_utils/np_browser.h @@ -0,0 +1,95 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_BROWSER_H_ +#define GPU_NP_UTILS_NP_BROWSER_H_ + +#include "base/basictypes.h" +#include "gpu/np_utils/np_headers.h" + +typedef struct _NPNetscapeFuncs NPNetscapeFuncs; + +namespace gpu_plugin { + +// This class exposes the functions provided by the browser to a plugin (the +// ones prefixed NPN_). +class NPBrowser { + public: + explicit NPBrowser(NPNetscapeFuncs* funcs); + virtual ~NPBrowser(); + + static NPBrowser* get() { + return browser_; + } + + // Standard functions from NPNetscapeFuncs. + + virtual NPIdentifier GetStringIdentifier(const NPUTF8* name); + + virtual void* MemAlloc(size_t size); + + virtual void MemFree(void* p); + + virtual NPObject* CreateObject(NPP npp, const NPClass* cl); + + virtual NPObject* RetainObject(NPObject* object); + + virtual void ReleaseObject(NPObject* object); + + virtual void ReleaseVariantValue(NPVariant* variant); + + virtual bool HasProperty(NPP npp, + NPObject* object, + NPIdentifier name); + + virtual bool GetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + NPVariant* result); + + virtual bool SetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* result); + + virtual bool RemoveProperty(NPP npp, + NPObject* object, + NPIdentifier name); + + virtual bool HasMethod(NPP npp, + NPObject* object, + NPIdentifier name); + + virtual bool Invoke(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result); + + virtual NPObject* GetWindowNPObject(NPP npp); + + typedef void (*PluginThreadAsyncCallProc)(void* data); + virtual void PluginThreadAsyncCall(NPP npp, + PluginThreadAsyncCallProc callback, + void* data); + + typedef void (*TimerProc)(NPP npp, uint32 timer_id); + virtual uint32 ScheduleTimer(NPP npp, + uint32 interval, + bool repeat, + TimerProc callback); + + virtual void UnscheduleTimer(NPP npp, uint32 timer_id); + + private: + static NPBrowser* browser_; + NPBrowser* previous_browser_; + NPNetscapeFuncs* netscape_funcs_; + DISALLOW_COPY_AND_ASSIGN(NPBrowser); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_BROWSER_H_ diff --git a/o3d/gpu/np_utils/np_browser_mock.h b/o3d/gpu/np_utils/np_browser_mock.h new file mode 100644 index 0000000..1ef574e --- /dev/null +++ b/o3d/gpu/np_utils/np_browser_mock.h @@ -0,0 +1,50 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_BROWSER_MOCK_H_ +#define GPU_NP_UTILS_NP_BROWSER_MOCK_H_ + +#include "gpu/np_utils/np_browser_stub.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace gpu_plugin { + +// This mocks certain member functions of the stub browser. Those relating +// to identifiers, memory management, reference counting and forwarding to +// NPObjects are deliberately not mocked so the mock browser can be used as +// normal for these calls. +class MockNPBrowser : public StubNPBrowser { + public: + NPObject* ConcreteCreateObject(NPP npp, const NPClass* cl) { + return StubNPBrowser::CreateObject(npp, cl); + } + + MockNPBrowser() { + // Do not mock CreateObject by default but allow it to be mocked so object + // creation can be intercepted. + ON_CALL(*this, CreateObject(testing::_, testing::_)) + .WillByDefault(testing::Invoke(this, + &MockNPBrowser::ConcreteCreateObject)); + } + + void ConcretePluginThreadAsyncCall(NPP npp, + PluginThreadAsyncCallProc callback, + void* data) { + return StubNPBrowser::PluginThreadAsyncCall(npp, callback, data); + } + + MOCK_METHOD2(CreateObject, NPObject*(NPP npp, const NPClass* cl)); + MOCK_METHOD1(GetWindowNPObject, NPObject*(NPP cpp)); + MOCK_METHOD3(PluginThreadAsyncCall, + void(NPP npp, PluginThreadAsyncCallProc callback, void* data)); + MOCK_METHOD4(ScheduleTimer, uint32(NPP npp, + uint32 interval, + bool repeat, + TimerProc callback)); + MOCK_METHOD2(UnscheduleTimer, void(NPP npp, uint32 timer_id)); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_BROWSER_MOCK_H_ diff --git a/o3d/gpu/np_utils/np_browser_stub.cc b/o3d/gpu/np_utils/np_browser_stub.cc new file mode 100644 index 0000000..2bc1c6a --- /dev/null +++ b/o3d/gpu/np_utils/np_browser_stub.cc @@ -0,0 +1,125 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/np_browser_stub.h" +#include "base/logging.h" +#include "base/message_loop.h" + +namespace gpu_plugin { + +StubNPBrowser::StubNPBrowser() : NPBrowser(NULL) { +} + +StubNPBrowser::~StubNPBrowser() { +} + +NPIdentifier StubNPBrowser::GetStringIdentifier(const NPUTF8* name) { + static std::set<std::string> names; + std::set<std::string>::iterator it = names.find(name); + if (it == names.end()) { + it = names.insert(name).first; + } + return const_cast<NPUTF8*>((*it).c_str()); +} + +void* StubNPBrowser::MemAlloc(size_t size) { + return malloc(size); +} + +void StubNPBrowser::MemFree(void* p) { + free(p); +} + +NPObject* StubNPBrowser::CreateObject(NPP npp, const NPClass* cl) { + NPObject* object = cl->allocate(npp, const_cast<NPClass*>(cl)); + object->referenceCount = 1; + object->_class = const_cast<NPClass*>(cl); + return object; +} + +NPObject* StubNPBrowser::RetainObject(NPObject* object) { + ++object->referenceCount; + return object; +} + +void StubNPBrowser::ReleaseObject(NPObject* object) { + DCHECK_GE(object->referenceCount, 0u); + --object->referenceCount; + if (object->referenceCount == 0) { + object->_class->deallocate(object); + } +} + +void StubNPBrowser::ReleaseVariantValue(NPVariant* variant) { + if (NPVARIANT_IS_STRING(*variant)) { + MemFree(const_cast<NPUTF8*>(variant->value.stringValue.UTF8Characters)); + } else if (NPVARIANT_IS_OBJECT(*variant)) { + ReleaseObject(NPVARIANT_TO_OBJECT(*variant)); + } +} + +bool StubNPBrowser::HasProperty(NPP npp, + NPObject* object, + NPIdentifier name) { + return object->_class->hasProperty(object, name); +} + +bool StubNPBrowser::GetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + NPVariant* result) { + return object->_class->getProperty(object, name, result); +} + +bool StubNPBrowser::SetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* result) { + return object->_class->setProperty(object, name, result); +} + +bool StubNPBrowser::RemoveProperty(NPP npp, + NPObject* object, + NPIdentifier name) { + return object->_class->removeProperty(object, name); +} + +bool StubNPBrowser::HasMethod(NPP npp, + NPObject* object, + NPIdentifier name) { + return object->_class->hasMethod(object, name); +} + +bool StubNPBrowser::Invoke(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return object->_class->invoke(object, name, args, num_args, result); +} + +NPObject* StubNPBrowser::GetWindowNPObject(NPP npp) { + return NULL; +} + +void StubNPBrowser::PluginThreadAsyncCall( + NPP npp, + PluginThreadAsyncCallProc callback, + void* data) { + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(callback, data)); +} + +uint32 StubNPBrowser::ScheduleTimer(NPP npp, + uint32 interval, + bool repeat, + TimerProc callback) { + return 0; +} + +void StubNPBrowser::UnscheduleTimer(NPP npp, uint32 timer_id) { +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_browser_stub.h b/o3d/gpu/np_utils/np_browser_stub.h new file mode 100644 index 0000000..b18d52d --- /dev/null +++ b/o3d/gpu/np_utils/np_browser_stub.h @@ -0,0 +1,84 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_BROWSER_STUB_H_ +#define GPU_NP_UTILS_NP_BROWSER_STUB_H_ + +#include <set> +#include <string> + +#include "gpu/np_utils/np_browser.h" + +namespace gpu_plugin { + +// Simple implementation of subset of the NPN functions for testing. +class StubNPBrowser : public NPBrowser { + public: + StubNPBrowser(); + virtual ~StubNPBrowser(); + + // Standard functions from NPNetscapeFuncs. + + virtual NPIdentifier GetStringIdentifier(const NPUTF8* name); + + virtual void* MemAlloc(size_t size); + + virtual void MemFree(void* p); + + virtual NPObject* CreateObject(NPP npp, const NPClass* cl); + + virtual NPObject* RetainObject(NPObject* object); + + virtual void ReleaseObject(NPObject* object); + + virtual void ReleaseVariantValue(NPVariant* variant); + + virtual bool HasProperty(NPP npp, + NPObject* object, + NPIdentifier name); + + virtual bool GetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + NPVariant* result); + + virtual bool SetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* result); + + virtual bool RemoveProperty(NPP npp, + NPObject* object, + NPIdentifier name); + + virtual bool HasMethod(NPP npp, + NPObject* object, + NPIdentifier name); + virtual bool Invoke(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result); + + virtual NPObject* GetWindowNPObject(NPP npp); + + virtual void PluginThreadAsyncCall(NPP npp, + PluginThreadAsyncCallProc callback, + void* data); + + virtual uint32 ScheduleTimer(NPP npp, + uint32 interval, + bool repeat, + TimerProc callback); + + virtual void UnscheduleTimer(NPP npp, uint32 timer_id); + + private: + DISALLOW_COPY_AND_ASSIGN(StubNPBrowser); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_BROWSER_STUB_H_ diff --git a/o3d/gpu/np_utils/np_class.h b/o3d/gpu/np_utils/np_class.h new file mode 100644 index 0000000..113f493 --- /dev/null +++ b/o3d/gpu/np_utils/np_class.h @@ -0,0 +1,125 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_CLASS_H_ +#define GPU_NP_UTILS_NP_CLASS_H_ + +#include "gpu/np_utils/np_object_pointer.h" +#include "gpu/np_utils/np_headers.h" + +// This file implements NPGetClass<T>. This function returns an NPClass +// that can be used to instantiate an NPObject subclass T. The NPClass +// function pointers will invoke the most derived corresponding member +// functions in T. + +namespace gpu_plugin { + +namespace np_class_impl { + // This template version of the NPClass allocate function creates a subclass + // of BaseNPObject. + template <typename NPObjectType> + static NPObject* Allocate(NPP npp, NPClass*) { + return new NPObjectType(npp); + } + + // These implementations of the NPClass functions forward to the virtual + // functions in DefaultNPObject. + template <typename NPObjectType> + static void Deallocate(NPObject* object) { + delete static_cast<NPObjectType*>(object); + } + + template <typename NPObjectType> + static void Invalidate(NPObject* object) { + return static_cast<NPObjectType*>(object)->Invalidate(); + } + + template <typename NPObjectType> + static bool HasMethod(NPObject* object, NPIdentifier name) { + return static_cast<NPObjectType*>(object)->HasMethod(name); + } + + template <typename NPObjectType> + static bool Invoke(NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return static_cast<NPObjectType*>(object)->Invoke( + name, args, num_args, result); + } + + template <typename NPObjectType> + static bool InvokeDefault(NPObject* object, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return static_cast<NPObjectType*>(object)->InvokeDefault( + args, num_args, result); + } + + template <typename NPObjectType> + static bool HasProperty(NPObject* object, NPIdentifier name) { + return static_cast<NPObjectType*>(object)->HasProperty(name); + } + + template <typename NPObjectType> + static bool GetProperty(NPObject* object, + NPIdentifier name, + NPVariant* result) { + return static_cast<NPObjectType*>(object)->GetProperty(name, result); + } + + template <typename NPObjectType> + static bool SetProperty(NPObject* object, + NPIdentifier name, + const NPVariant* value) { + return static_cast<NPObjectType*>(object)->SetProperty(name, value); + } + + template <typename NPObjectType> + static bool RemoveProperty(NPObject* object, NPIdentifier name) { + return static_cast<NPObjectType*>(object)->RemoveProperty(name); + } + + template <typename NPObjectType> + static bool Enumerate(NPObject* object, + NPIdentifier** names, + uint32_t* count) { + return static_cast<NPObjectType*>(object)->Enumerate(names, count); + }; + + template <typename NPObjectType> + static bool Construct(NPObject* object, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return static_cast<NPObjectType*>(object)->Construct( + args, num_args, result); + } +} // namespace np_class_impl; + +template <typename NPObjectType> +const NPClass* NPGetClass() { + static const NPClass np_class = { + NP_CLASS_STRUCT_VERSION, + np_class_impl::Allocate<NPObjectType>, + np_class_impl::Deallocate<NPObjectType>, + np_class_impl::Invalidate<NPObjectType>, + np_class_impl::HasMethod<NPObjectType>, + np_class_impl::Invoke<NPObjectType>, + np_class_impl::InvokeDefault<NPObjectType>, + np_class_impl::HasProperty<NPObjectType>, + np_class_impl::GetProperty<NPObjectType>, + np_class_impl::SetProperty<NPObjectType>, + np_class_impl::RemoveProperty<NPObjectType>, + np_class_impl::Enumerate<NPObjectType>, + np_class_impl::Construct<NPObjectType>, + }; + return &np_class; +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_CLASS_H_ diff --git a/o3d/gpu/np_utils/np_class_unittest.cc b/o3d/gpu/np_utils/np_class_unittest.cc new file mode 100644 index 0000000..0db632b --- /dev/null +++ b/o3d/gpu/np_utils/np_class_unittest.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/np_class.h" +#include "gpu/np_utils/np_object_mock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::StrictMock; + +namespace gpu_plugin { + +class NPClassTest : public testing::Test { + protected: + virtual void SetUp() { + np_class = NPGetClass<StrictMock<MockNPObject> >(); + + // Dummy identifier is never used with real NPAPI so it can point to + // anything. + identifier = this; + } + + virtual void TearDown() { + } + + NPP_t npp_; + const NPClass* np_class; + NPIdentifier identifier; + NPVariant args[3]; + NPVariant result; +}; + +TEST_F(NPClassTest, AllocateAndDeallocateObject) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + EXPECT_TRUE(NULL != object); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, InvalidateForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, Invalidate()); + np_class->invalidate(object); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, HasMethodForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, HasMethod(identifier)); + np_class->hasMethod(object, identifier); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, InvokeForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, Invoke(identifier, args, 3, &result)); + np_class->invoke(object, identifier, args, 3, &result); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, InvokeDefaultForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, InvokeDefault(args, 3, &result)); + np_class->invokeDefault(object, args, 3, &result); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, HasPropertyForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, HasProperty(identifier)); + np_class->hasProperty(object, identifier); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, GetPropertyForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, GetProperty(identifier, &result)); + np_class->getProperty(object, identifier, &result); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, SetPropertyForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, SetProperty(identifier, &result)); + np_class->setProperty(object, identifier, &result); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, RemovePropertyForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, RemoveProperty(identifier)); + np_class->removeProperty(object, identifier); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, EnumerateForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + NPIdentifier* identifier = NULL; + uint32_t count; + EXPECT_CALL(*object, Enumerate(&identifier, &count)); + np_class->enumerate(object, &identifier, &count); + + np_class->deallocate(object); +} + +TEST_F(NPClassTest, ConstructForwards) { + MockNPObject* object = static_cast<MockNPObject*>( + np_class->allocate(&npp_, const_cast<NPClass*>(np_class))); + + EXPECT_CALL(*object, Construct(args, 3, &result)); + np_class->construct(object, args, 3, &result); + + np_class->deallocate(object); +} +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_dispatcher.cc b/o3d/gpu/np_utils/np_dispatcher.cc new file mode 100644 index 0000000..7859f8b --- /dev/null +++ b/o3d/gpu/np_utils/np_dispatcher.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/np_dispatcher.h" + +namespace gpu_plugin { + +bool DispatcherHasMethodHelper(BaseNPDispatcher* chain, + NPObject* object, + NPIdentifier name) { + for (BaseNPDispatcher* dispatcher = chain; + dispatcher; + dispatcher = dispatcher->next()) { + if (dispatcher->name() == name) { + return true; + } + } + + return false; +} + +bool DispatcherInvokeHelper(BaseNPDispatcher* chain, + NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + VOID_TO_NPVARIANT(*result); + + for (BaseNPDispatcher* dispatcher = chain; + dispatcher; + dispatcher = dispatcher->next()) { + if (dispatcher->name() == name && + dispatcher->num_args() == static_cast<int>(num_args)) { + if (dispatcher->Invoke(object, args, num_args, result)) + return true; + } + } + + return false; +} + +bool DispatcherEnumerateHelper(BaseNPDispatcher* chain, + NPObject* object, + NPIdentifier** names, + uint32_t* num_names) { + // Count the number of names. + *num_names = 0; + for (BaseNPDispatcher* dispatcher = chain; + dispatcher; + dispatcher = dispatcher->next()) { + ++(*num_names); + } + + // Copy names into the array. + *names = static_cast<NPIdentifier*>( + NPBrowser::get()->MemAlloc((*num_names) * sizeof(**names))); + int i = 0; + for (BaseNPDispatcher* dispatcher = chain; + dispatcher; + dispatcher = dispatcher->next()) { + (*names)[i] = dispatcher->name(); + ++i; + } + + return true; +} + +BaseNPDispatcher::BaseNPDispatcher(BaseNPDispatcher* next, const NPUTF8* name) + : next_(next) { + // Convert first character to lower case if it is the ASCII range. + // TODO(apatrick): do this correctly for non-ASCII characters. + std::string java_script_style_name(name); + if (isupper(java_script_style_name[0])) { + java_script_style_name[0] = tolower(java_script_style_name[0]); + } + + name_ = NPBrowser::get()->GetStringIdentifier( + java_script_style_name.c_str()); +} + +BaseNPDispatcher::~BaseNPDispatcher() { +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_dispatcher.h b/o3d/gpu/np_utils/np_dispatcher.h new file mode 100644 index 0000000..5b84f86 --- /dev/null +++ b/o3d/gpu/np_utils/np_dispatcher.h @@ -0,0 +1,222 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_DISPATCHER_H_ +#define GPU_NP_UTILS_NP_DISPATCHER_H_ + +#include <string> + +#include "gpu/np_utils/np_utils.h" +#include "gpu/np_utils/np_headers.h" + +// Dispatchers make regular member functions available as NPObject methods. +// Usage: +// +// class MyNPObject : public DefaultNPObject<NPObject> { +// public: +// int MyMethod(bool a, float b); +// NP_UTILS_BEGIN_DISPATCHER_CHAIN(MyNPObject, DispatchedNPObject) +// NP_UTILS_DISPATCHER(MyMethod, int(bool, float)) +// NP_UTILS_END_DISPATCHER_CHAIN +// }; +// +// Multiple member functions may be listed in the dispatcher chain. Inheritance +// is supported. The following types are supported as return types and parameter +// types: +// * bool +// * int +// * float +// * double +// * std::string +// * NPObject* +// + +// These macros are used to make dispatcher chains. +#define NP_UTILS_NP_UTILS_DISPATCHER_JOIN2(a, b) a ## b +#define NP_UTILS_DISPATCHER_JOIN(a, b) NP_UTILS_NP_UTILS_DISPATCHER_JOIN2(a, b) +#define NP_UTILS_DISPATCHER_UNIQUE \ + NP_UTILS_DISPATCHER_JOIN(dispatcher, __LINE__) + +#define NP_UTILS_BEGIN_DISPATCHER_CHAIN(Class, BaseClass) \ + static BaseNPDispatcher* GetDispatcherChain() { \ + typedef Class ThisClass; \ + BaseNPDispatcher* top_dispatcher = BaseClass::GetDispatcherChain(); \ + +#define NP_UTILS_DISPATCHER(name, Signature) \ + static NPDispatcher<ThisClass, Signature> \ + NP_UTILS_DISPATCHER_UNIQUE( \ + top_dispatcher, \ + #name, \ + &ThisClass::name); \ + top_dispatcher = &NP_UTILS_DISPATCHER_UNIQUE; \ + +#define NP_UTILS_END_DISPATCHER_CHAIN \ + return top_dispatcher; \ + } \ + bool HasMethod(NPIdentifier name) { \ + return DispatcherHasMethodHelper(GetDispatcherChain(), this, name); \ + } \ + bool Invoke(NPIdentifier name, \ + const NPVariant* args, \ + uint32_t num_args, \ + NPVariant* result) { \ + return DispatcherInvokeHelper(GetDispatcherChain(), \ + this, \ + name, \ + args, \ + num_args, \ + result); \ + } \ + bool Enumerate(NPIdentifier** names, uint32_t* num_names) { \ + return DispatcherEnumerateHelper(GetDispatcherChain(), \ + this, \ + names, \ + num_names); \ + } \ + +namespace gpu_plugin { + +class BaseNPDispatcher { + public: + BaseNPDispatcher(BaseNPDispatcher* next, const NPUTF8* name); + + virtual ~BaseNPDispatcher(); + + BaseNPDispatcher* next() const { + return next_; + } + + NPIdentifier name() const { + return name_; + } + + virtual int num_args() const = 0; + + virtual bool Invoke(NPObject* object, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) = 0; + + private: + BaseNPDispatcher* next_; + NPIdentifier name_; + DISALLOW_COPY_AND_ASSIGN(BaseNPDispatcher); +}; + +bool DispatcherHasMethodHelper(BaseNPDispatcher* chain, + NPObject* object, + NPIdentifier name); + +bool DispatcherInvokeHelper(BaseNPDispatcher* chain, + NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result); + +bool DispatcherEnumerateHelper(BaseNPDispatcher* chain, + NPObject* object, + NPIdentifier** names, + uint32_t* num_names); + +// This class should never be instantiated. It is always specialized. Attempting +// to instantiate it results in a compilation error. This might mean an +// attempt to instantiate a dispatcher with more parameters than have been +// specialized for. See the specialization code below. +template <typename NPObjectType, typename FunctionType> +struct NPDispatcher { +}; + +#define TO_NPVARIANT(index) \ + T##index n##index; \ + if (!NPVariantToValue(&n##index, args[index])) \ + return false; \ + +#define NUM_PARAMS 0 +#define PARAM_TYPENAMES +#define PARAM_TYPES +#define PARAM_NAMES +#define PARAM_DECLS // NOLINT + +#define PARAM_TO_NVPARIANT_CONVERSIONS \ + +#include "gpu/np_utils/np_dispatcher_specializations.h" // NOLINT + + +#define NUM_PARAMS 1 +#define PARAM_TYPENAMES , typename T0 +#define PARAM_TYPES T0 +#define PARAM_NAMES n0 +#define PARAM_DECLS T0 n0; // NOLINT + +#define PARAM_TO_NVPARIANT_CONVERSIONS \ + TO_NPVARIANT(0); \ + +#include "gpu/np_utils/np_dispatcher_specializations.h" // NOLINT + + +#define NUM_PARAMS 2 +#define PARAM_TYPENAMES , typename T0, typename T1 +#define PARAM_TYPES T0, T1 +#define PARAM_NAMES n0, n1 +#define PARAM_DECLS T0 n0; T1 n1; // NOLINT + +#define PARAM_TO_NVPARIANT_CONVERSIONS \ + TO_NPVARIANT(0); \ + TO_NPVARIANT(1); \ + +#include "gpu/np_utils/np_dispatcher_specializations.h" // NOLINT + + +#define NUM_PARAMS 3 +#define PARAM_TYPENAMES , typename T0, typename T1, typename T2 +#define PARAM_TYPES T0, T1, T2 +#define PARAM_NAMES n0, n1, n2 +#define PARAM_DECLS T0 n0; T1 n1; T2 n2; // NOLINT + +#define PARAM_TO_NVPARIANT_CONVERSIONS \ + TO_NPVARIANT(0); \ + TO_NPVARIANT(1); \ + TO_NPVARIANT(2); \ + +#include "gpu/np_utils/np_dispatcher_specializations.h" // NOLINT + + +#define NUM_PARAMS 4 +#define PARAM_TYPENAMES , typename T0, typename T1, typename T2, typename T3 +#define PARAM_TYPES T0, T1, T2, T3 +#define PARAM_NAMES n0, n1, n2, n3 +#define PARAM_DECLS T0 n0; T1 n1; T2 n2; T3 n3; // NOLINT + +#define PARAM_TO_NVPARIANT_CONVERSIONS \ + TO_NPVARIANT(0); \ + TO_NPVARIANT(1); \ + TO_NPVARIANT(2); \ + TO_NPVARIANT(3); \ + +#include "gpu/np_utils/np_dispatcher_specializations.h" // NOLINT + + +#define NUM_PARAMS 5 +#define PARAM_TYPENAMES , typename T0, typename T1, typename T2, typename T3, \ + typename T4 +#define PARAM_TYPES T0, T1, T2, T3, T4 +#define PARAM_NAMES n0, n1, n2, n3, n4 +#define PARAM_DECLS T0 n0; T1 n1; T2 n2; T3 n3; T4 n4; // NOLINT + +#define PARAM_TO_NVPARIANT_CONVERSIONS \ + TO_NPVARIANT(0); \ + TO_NPVARIANT(1); \ + TO_NPVARIANT(2); \ + TO_NPVARIANT(3); \ + TO_NPVARIANT(4); \ + +#include "gpu/np_utils/np_dispatcher_specializations.h" // NOLINT + + +#undef TO_NPVARIANT + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_DISPATCHER_H_ diff --git a/o3d/gpu/np_utils/np_dispatcher_specializations.h b/o3d/gpu/np_utils/np_dispatcher_specializations.h new file mode 100644 index 0000000..62fb8c4 --- /dev/null +++ b/o3d/gpu/np_utils/np_dispatcher_specializations.h @@ -0,0 +1,85 @@ +// Copyright (c) 2006-2008 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. + +// There is deliberately no header guard here. This file is included multiple +// times, once for each dispatcher specialiation arity. Do not include this +// file directly. Include np_dispatcher.h instead. + +template <typename NPObjectType PARAM_TYPENAMES> +class NPDispatcher<NPObjectType, void(PARAM_TYPES)> + : public BaseNPDispatcher { + typedef void (NPObjectType::*FunctionType)(PARAM_TYPES); + public: + NPDispatcher(BaseNPDispatcher* next, + const NPUTF8* name, + FunctionType function) + : BaseNPDispatcher(next, name), + function_(function) { + } + + virtual bool Invoke(NPObject* object, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + VOID_TO_NPVARIANT(*result); + + if (num_args != NUM_PARAMS) + return false; + + PARAM_TO_NVPARIANT_CONVERSIONS + + (static_cast<NPObjectType*>(object)->*function_)(PARAM_NAMES); + return true; + } + + virtual int num_args() const { + return NUM_PARAMS; + } + + private: + FunctionType function_; +}; + +template <typename NPObjectType, typename R PARAM_TYPENAMES> +class NPDispatcher<NPObjectType, R(PARAM_TYPES)> + : public BaseNPDispatcher { + typedef R (NPObjectType::*FunctionType)(PARAM_TYPES); + public: + NPDispatcher(BaseNPDispatcher* next, + const NPUTF8* name, + FunctionType function) + : BaseNPDispatcher(next, name), + function_(function) { + } + + virtual bool Invoke(NPObject* object, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + VOID_TO_NPVARIANT(*result); + + if (num_args != NUM_PARAMS) + return false; + + PARAM_TO_NVPARIANT_CONVERSIONS + + ValueToNPVariant( + (static_cast<NPObjectType*>(object)->*function_)(PARAM_NAMES), result); + return true; + } + + virtual int num_args() const { + return NUM_PARAMS; + } + + private: + FunctionType function_; +}; + +#undef NUM_PARAMS +#undef PARAM_TYPENAMES +#undef PARAM_TYPES +#undef PARAM_NAMES +#undef PARAM_DECLS +#undef PARAM_TO_NVPARIANT_CONVERSIONS diff --git a/o3d/gpu/np_utils/np_headers.h b/o3d/gpu/np_utils/np_headers.h new file mode 100644 index 0000000..5f60d8e --- /dev/null +++ b/o3d/gpu/np_utils/np_headers.h @@ -0,0 +1,16 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_HEADERS_H_ +#define GPU_NP_UTILS_NP_HEADERS_H_ + +#if defined(O3D_IN_CHROME) +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" +#else +#include "o3d/third_party/npapi/include/npapi.h" +#include "o3d/third_party/npapi/include/npruntime.h" +#endif + +#endif // GPU_NP_UTILS_NP_HEADERS_H_ diff --git a/o3d/gpu/np_utils/np_object_mock.h b/o3d/gpu/np_utils/np_object_mock.h new file mode 100644 index 0000000..8c3d0e7 --- /dev/null +++ b/o3d/gpu/np_utils/np_object_mock.h @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_OBJECT_MOCK_H_ +#define GPU_NP_UTILS_NP_OBJECT_MOCK_H_ + +#include "gpu/np_utils/np_browser.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace gpu_plugin { + +class MockNPObject : public NPObject { + public: + explicit MockNPObject(NPP npp) { + } + + MOCK_METHOD0(Invalidate, void()); + MOCK_METHOD1(HasMethod, bool(NPIdentifier)); + MOCK_METHOD4(Invoke, + bool(NPIdentifier, const NPVariant*, uint32_t, NPVariant*)); + MOCK_METHOD3(InvokeDefault, bool(const NPVariant*, uint32_t, NPVariant*)); + MOCK_METHOD1(HasProperty, bool(NPIdentifier)); + MOCK_METHOD2(GetProperty, bool(NPIdentifier, NPVariant*)); + MOCK_METHOD2(SetProperty, bool(NPIdentifier, const NPVariant*)); + MOCK_METHOD1(RemoveProperty, bool(NPIdentifier)); + MOCK_METHOD2(Enumerate, bool(NPIdentifier**, uint32_t*)); + MOCK_METHOD3(Construct, bool(const NPVariant*, uint32_t, NPVariant*)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockNPObject); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_OBJECT_MOCK_H_ diff --git a/o3d/gpu/np_utils/np_object_pointer.h b/o3d/gpu/np_utils/np_object_pointer.h new file mode 100644 index 0000000..a8987be --- /dev/null +++ b/o3d/gpu/np_utils/np_object_pointer.h @@ -0,0 +1,119 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_OBJECT_POINTER_H_ +#define GPU_NP_UTILS_NP_OBJECT_POINTER_H_ + +#include "base/logging.h" +#include "gpu/np_utils/np_browser.h" +#include "gpu/np_utils/np_headers.h" + +namespace gpu_plugin { + +// Smart pointer for NPObjects that automatically handles reference counting. +template <typename NPObjectType> +class NPObjectPointer { + public: + NPObjectPointer() : object_(NULL) {} + + NPObjectPointer(const NPObjectPointer& rhs) : object_(rhs.object_) { + Retain(); + } + + explicit NPObjectPointer(NPObjectType* p) : object_(p) { + Retain(); + } + + template <typename RHS> + NPObjectPointer(const NPObjectPointer<RHS>& rhs) : object_(rhs.Get()) { + Retain(); + } + + ~NPObjectPointer() { + Release(); + } + + NPObjectPointer& operator=(const NPObjectPointer& rhs) { + if (object_ == rhs.Get()) + return *this; + + Release(); + object_ = rhs.object_; + Retain(); + return *this; + } + + template <typename RHS> + NPObjectPointer& operator=(const NPObjectPointer<RHS>& rhs) { + if (object_ == rhs.Get()) + return *this; + + Release(); + object_ = rhs.Get(); + Retain(); + return *this; + } + + template <class RHS> + bool operator==(const NPObjectPointer<RHS>& rhs) const { + return object_ == rhs.Get(); + } + + template <class RHS> + bool operator!=(const NPObjectPointer<RHS>& rhs) const { + return object_ != rhs.Get(); + } + + // The NPObject convention for returning an NPObject pointer from a function + // is that the caller is responsible for releasing the reference count. + static NPObjectPointer FromReturned(NPObjectType* p) { + NPObjectPointer pointer(p); + pointer.Release(); + return pointer; + } + + // The NPObject convention for returning an NPObject pointer from a function + // is that the caller is responsible for releasing the reference count. + NPObjectType* ToReturned() const { + Retain(); + return object_; + } + + NPObjectType* Get() const { + return object_; + } + + NPObjectType* operator->() const { + return object_; + } + + NPObjectType& operator*() const { + return *object_; + } + + private: + void Retain() const { + if (object_) { + NPBrowser::get()->RetainObject(object_); + } + } + + void Release() const { + if (object_) { + NPBrowser::get()->ReleaseObject(object_); + } + } + + NPObjectType* object_; +}; + +// For test diagnostics. +template <typename NPObjectType> +std::ostream& operator<<(std::ostream& stream, + const NPObjectPointer<NPObjectType>& pointer) { + return stream << pointer.Get(); +} +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_OBJECT_POINTER_H_ diff --git a/o3d/gpu/np_utils/np_object_pointer_unittest.cc b/o3d/gpu/np_utils/np_object_pointer_unittest.cc new file mode 100644 index 0000000..c14f6ed --- /dev/null +++ b/o3d/gpu/np_utils/np_object_pointer_unittest.cc @@ -0,0 +1,220 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/np_class.h" +#include "gpu/np_utils/np_object_mock.h" +#include "gpu/np_utils/np_browser_stub.h" +#include "gpu/np_utils/np_object_pointer.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Return; +using testing::StrictMock; + +namespace gpu_plugin { + +class DerivedNPObject : public MockNPObject { + public: + explicit DerivedNPObject(NPP npp) : MockNPObject(npp) { + } +}; + +class NPObjectPointerTest : public testing::Test { + protected: + virtual void SetUp() { + np_class_ = NPGetClass<StrictMock<MockNPObject> >(); + + raw_pointer_ = static_cast<MockNPObject*>( + NPBrowser::get()->CreateObject(NULL, np_class_)); + + raw_derived_pointer_ = static_cast<DerivedNPObject*>( + NPBrowser::get()->CreateObject(NULL, np_class_)); + } + + virtual void TearDown() { + NPBrowser::get()->ReleaseObject(raw_pointer_); + NPBrowser::get()->ReleaseObject(raw_derived_pointer_); + } + + StubNPBrowser stub_browser_; + const NPClass* np_class_; + MockNPObject* raw_pointer_; + DerivedNPObject* raw_derived_pointer_; +}; + +TEST_F(NPObjectPointerTest, PointerIsNullByDefault) { + NPObjectPointer<MockNPObject> p; + ASSERT_TRUE(NULL == p.Get()); +} + +TEST_F(NPObjectPointerTest, PointerCanBeExplicitlyConstructedFromRawPointer) { + EXPECT_EQ(1, raw_pointer_->referenceCount); + { + NPObjectPointer<MockNPObject> p(raw_pointer_); + ASSERT_TRUE(raw_pointer_ == p.Get()); + EXPECT_EQ(2, raw_pointer_->referenceCount); + } + EXPECT_EQ(1, raw_pointer_->referenceCount); +} + +TEST_F(NPObjectPointerTest, + PointerCanBeExplicitlyConstructedFromNullRawPointer) { + NPObjectPointer<MockNPObject> p(NULL); + ASSERT_TRUE(NULL == p.Get()); +} + +TEST_F(NPObjectPointerTest, PointerCanBeCopyConstructed) { + NPObjectPointer<MockNPObject> p1(raw_pointer_); + EXPECT_EQ(2, raw_pointer_->referenceCount); + { + NPObjectPointer<MockNPObject> p2(p1); + ASSERT_TRUE(raw_pointer_ == p2.Get()); + EXPECT_EQ(3, raw_pointer_->referenceCount); + } + EXPECT_EQ(2, raw_pointer_->referenceCount); +} + +TEST_F(NPObjectPointerTest, PointerCanBeConstructedFromDerived) { + NPObjectPointer<DerivedNPObject> p1(raw_derived_pointer_); + EXPECT_EQ(2, raw_derived_pointer_->referenceCount); + { + NPObjectPointer<MockNPObject> p2(p1); + ASSERT_TRUE(raw_derived_pointer_ == p2.Get()); + EXPECT_EQ(3, raw_derived_pointer_->referenceCount); + } + EXPECT_EQ(2, raw_derived_pointer_->referenceCount); +} + +TEST_F(NPObjectPointerTest, + PointerCanBeCopyConstructedFromNull) { + NPObjectPointer<MockNPObject> p(NULL); + ASSERT_TRUE(NULL == p.Get()); +} + +TEST_F(NPObjectPointerTest, PointerCanBeAssigned) { + NPObjectPointer<MockNPObject> p1(raw_pointer_); + EXPECT_EQ(2, raw_pointer_->referenceCount); + { + NPObjectPointer<MockNPObject> p2; + p2 = p1; + ASSERT_TRUE(raw_pointer_ == p2.Get()); + EXPECT_EQ(3, raw_pointer_->referenceCount); + + p2 = NPObjectPointer<MockNPObject>(); + ASSERT_TRUE(NULL == p2.Get()); + EXPECT_EQ(2, raw_pointer_->referenceCount); + + p2 = p1; + ASSERT_TRUE(raw_pointer_ == p2.Get()); + EXPECT_EQ(3, raw_pointer_->referenceCount); + } + EXPECT_EQ(2, raw_pointer_->referenceCount); +} + +TEST_F(NPObjectPointerTest, PointerCanBeAssignedToSelf) { + NPObjectPointer<MockNPObject> p(raw_pointer_); + NPBrowser::get()->ReleaseObject(raw_pointer_); + EXPECT_EQ(1, raw_pointer_->referenceCount); + p = p; + EXPECT_EQ(1, raw_pointer_->referenceCount); + NPBrowser::get()->RetainObject(raw_pointer_); +} + +TEST_F(NPObjectPointerTest, PointerCanBeAssignedDerived) { + NPObjectPointer<DerivedNPObject> p1(raw_derived_pointer_); + EXPECT_EQ(2, raw_derived_pointer_->referenceCount); + { + NPObjectPointer<MockNPObject> p2; + p2 = p1; + ASSERT_TRUE(raw_derived_pointer_ == p2.Get()); + EXPECT_EQ(3, raw_derived_pointer_->referenceCount); + + p2 = NPObjectPointer<MockNPObject>(); + ASSERT_TRUE(NULL == p2.Get()); + EXPECT_EQ(2, raw_derived_pointer_->referenceCount); + + p2 = p1; + ASSERT_TRUE(raw_derived_pointer_ == p2.Get()); + EXPECT_EQ(3, raw_derived_pointer_->referenceCount); + } + EXPECT_EQ(2, raw_derived_pointer_->referenceCount); +} + +TEST_F(NPObjectPointerTest, DerivedPointerCanBeAssignedToSelf) { + NPObjectPointer<MockNPObject> p1(raw_derived_pointer_); + NPObjectPointer<DerivedNPObject> p2(raw_derived_pointer_); + NPBrowser::get()->ReleaseObject(raw_derived_pointer_); + NPBrowser::get()->ReleaseObject(raw_derived_pointer_); + EXPECT_EQ(1, raw_derived_pointer_->referenceCount); + p1 = p2; + EXPECT_EQ(1, raw_derived_pointer_->referenceCount); + NPBrowser::get()->RetainObject(raw_derived_pointer_); + NPBrowser::get()->RetainObject(raw_derived_pointer_); +} + +TEST_F(NPObjectPointerTest, CanComparePointersForEqual) { + NPObjectPointer<MockNPObject> p1(raw_pointer_); + NPObjectPointer<DerivedNPObject> p2(raw_derived_pointer_); + EXPECT_TRUE(p1 == p1); + EXPECT_FALSE(p1 == p2); + EXPECT_FALSE(p2 == p1); + EXPECT_FALSE(p1 == NPObjectPointer<MockNPObject>()); +} + +TEST_F(NPObjectPointerTest, CanComparePointersForNotEqual) { + NPObjectPointer<MockNPObject> p1(raw_pointer_); + NPObjectPointer<DerivedNPObject> p2(raw_derived_pointer_); + EXPECT_FALSE(p1 != p1); + EXPECT_TRUE(p1 != p2); + EXPECT_TRUE(p2 != p1); + EXPECT_TRUE(p1 != NPObjectPointer<MockNPObject>()); +} + +TEST_F(NPObjectPointerTest, ArrowOperatorCanBeUsedToAccessNPObjectMembers) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("hello"); + + EXPECT_CALL(*raw_pointer_, HasProperty(name)).WillOnce(Return(true)); + + NPObjectPointer<MockNPObject> p(raw_pointer_); + EXPECT_TRUE(p->HasProperty(name)); +} + +TEST_F(NPObjectPointerTest, StarOperatorReturnsNPObjectReference) { + NPObjectPointer<MockNPObject> p(raw_pointer_); + EXPECT_EQ(raw_pointer_, &*p); +} + +TEST_F(NPObjectPointerTest, PointerCanBeConstructedFromReturnedNPObject) { + NPBrowser::get()->RetainObject(raw_pointer_); + EXPECT_EQ(2, raw_pointer_->referenceCount); + { + NPObjectPointer<MockNPObject> p( + NPObjectPointer<MockNPObject>::FromReturned(raw_pointer_)); + EXPECT_EQ(2, raw_pointer_->referenceCount); + } + EXPECT_EQ(1, raw_pointer_->referenceCount); +} + +TEST_F(NPObjectPointerTest, PointerCanBeConstructedFromReturnedNullNPObject) { + NPObjectPointer<MockNPObject> p( + NPObjectPointer<MockNPObject>::FromReturned(NULL)); + EXPECT_TRUE(NULL == p.Get()); +} + +TEST_F(NPObjectPointerTest, PointerCanBeReturnedAsARawNPObject) { + NPObjectPointer<MockNPObject> p(raw_pointer_); + EXPECT_EQ(raw_pointer_, p.ToReturned()); + + // Check reference count is incremented before return for caller. + EXPECT_EQ(3, raw_pointer_->referenceCount); + + NPBrowser::get()->ReleaseObject(raw_pointer_); +} + +TEST_F(NPObjectPointerTest, NULLPointerCanBeReturnedAsARawNPObject) { + NPObjectPointer<MockNPObject> p; + EXPECT_TRUE(NULL == p.ToReturned()); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_plugin_object.h b/o3d/gpu/np_utils/np_plugin_object.h new file mode 100644 index 0000000..063079f --- /dev/null +++ b/o3d/gpu/np_utils/np_plugin_object.h @@ -0,0 +1,50 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_PLUGIN_OBJECT_H_ +#define GPU_NP_UTILS_NP_PLUGIN_OBJECT_H_ + +#include "gpu/np_utils/np_object_pointer.h" +#include "gpu/np_utils/np_headers.h" + +namespace gpu_plugin { + +// Interface for a plugin instance. The NPP plugin calls forwards to an instance +// of this interface. +class PluginObject { + public: + // Initialize this object. + virtual NPError New(NPMIMEType plugin_type, + int16 argc, + char* argn[], + char* argv[], + NPSavedData* saved) = 0; + + virtual NPError SetWindow(NPWindow* new_window) = 0; + + virtual int16 HandleEvent(NPEvent* event) = 0; + + // Uninitialize but do not deallocate the object. Release will be called to + // deallocate if Destroy succeeds. + virtual NPError Destroy(NPSavedData** saved) = 0; + + // Deallocate this object. This object is invalid after this returns. + virtual void Release() = 0; + + virtual NPObject* GetScriptableNPObject() = 0; + + protected: + PluginObject() { + } + + virtual ~PluginObject() { + } + + private: + DISALLOW_COPY_AND_ASSIGN(PluginObject); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_PLUGIN_OBJECT_H_ diff --git a/o3d/gpu/np_utils/np_plugin_object_factory.cc b/o3d/gpu/np_utils/np_plugin_object_factory.cc new file mode 100644 index 0000000..831a4bd --- /dev/null +++ b/o3d/gpu/np_utils/np_plugin_object_factory.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2006-2008 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 "gpu/gpu_plugin/gpu_plugin_object_factory.h" +#include "base/logging.h" + +namespace gpu_plugin { + +NPPluginObjectFactory* NPPluginObjectFactory::factory_; + +PluginObject* NPPluginObjectFactory::CreatePluginObject( + NPP npp, + NPMIMEType plugin_type) { + return NULL; +} + +NPPluginObjectFactory::NPPluginObjectFactory() { + // Make this the first factory in the linked list. + previous_factory_ = factory_; + factory_ = this; +} + +NPPluginObjectFactory::~NPPluginObjectFactory() { + // Remove this factory from the linked list. + DCHECK(factory_ == this); + factory_ = previous_factory_; +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_plugin_object_factory.h b/o3d/gpu/np_utils/np_plugin_object_factory.h new file mode 100644 index 0000000..403d8d5 --- /dev/null +++ b/o3d/gpu/np_utils/np_plugin_object_factory.h @@ -0,0 +1,37 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_PLUGIN_OBJECT_FACTORY_H_ +#define GPU_NP_UTILS_NP_PLUGIN_OBJECT_FACTORY_H_ + +#include "base/basictypes.h" +#include "gpu/np_utils/np_headers.h" + +namespace gpu_plugin { + +class PluginObject; + +// Mockable factory base class used to create instances of PluginObject based on +// plugin mime type. +class NPPluginObjectFactory { + public: + virtual PluginObject* CreatePluginObject(NPP npp, NPMIMEType plugin_type); + + static NPPluginObjectFactory* get() { + return factory_; + } + + protected: + NPPluginObjectFactory(); + virtual ~NPPluginObjectFactory(); + + private: + static NPPluginObjectFactory* factory_; + NPPluginObjectFactory* previous_factory_; + DISALLOW_COPY_AND_ASSIGN(NPPluginObjectFactory); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_PLUGIN_OBJECT_FACTORY_H_ diff --git a/o3d/gpu/np_utils/np_plugin_object_factory_mock.h b/o3d/gpu/np_utils/np_plugin_object_factory_mock.h new file mode 100644 index 0000000..e15447a --- /dev/null +++ b/o3d/gpu/np_utils/np_plugin_object_factory_mock.h @@ -0,0 +1,23 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_PLUGIN_OBJECT_FACTORY_MOCK_H_ +#define GPU_NP_UTILS_NP_PLUGIN_OBJECT_FACTORY_MOCK_H_ + +#include "gpu/np_utils/np_plugin_object_factory.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gpu_plugin { + +// Mockable factory used to create instances of PluginObject based on plugin +// mime type. +class MockPluginObjectFactory : public NPPluginObjectFactory { + public: + MOCK_METHOD2(CreatePluginObject, PluginObject*(NPP, NPMIMEType)); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_PLUGIN_OBJECT_FACTORY_MOCK_H_ diff --git a/o3d/gpu/np_utils/np_plugin_object_mock.h b/o3d/gpu/np_utils/np_plugin_object_mock.h new file mode 100644 index 0000000..e67861b --- /dev/null +++ b/o3d/gpu/np_utils/np_plugin_object_mock.h @@ -0,0 +1,26 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_PLUGIN_OBJECT_MOCK_H_ +#define GPU_NP_UTILS_NP_PLUGIN_OBJECT_MOCK_H_ + +#include "gpu/np_utils/np_plugin_object.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gpu_plugin { + +class MockPluginObject : public PluginObject { + public: + MOCK_METHOD5(New, NPError(NPMIMEType, int16, char*[], char*[], NPSavedData*)); + MOCK_METHOD1(SetWindow, NPError(NPWindow*)); + MOCK_METHOD1(HandleEvent, int16(NPEvent*)); + MOCK_METHOD1(Destroy, NPError(NPSavedData**)); + MOCK_METHOD0(Release, void()); + MOCK_METHOD0(GetScriptableNPObject, NPObject*()); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_PLUGIN_OBJECT_MOCK_H_ diff --git a/o3d/gpu/np_utils/np_utils.cc b/o3d/gpu/np_utils/np_utils.cc new file mode 100644 index 0000000..03c4a20 --- /dev/null +++ b/o3d/gpu/np_utils/np_utils.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/np_utils.h" + +namespace gpu_plugin { + +bool NPVariantToValue(bool* value, const NPVariant& variant) { + if (NPVARIANT_IS_BOOLEAN(variant)) { + *value = NPVARIANT_TO_BOOLEAN(variant); + return true; + } + + return false; +} + +bool NPVariantToValue(int32* value, const NPVariant& variant) { + if (NPVARIANT_IS_INT32(variant)) { + *value = NPVARIANT_TO_INT32(variant); + return true; + } + + return false; +} + +bool NPVariantToValue(float* value, const NPVariant& variant) { + if (NPVARIANT_IS_DOUBLE(variant)) { + *value = static_cast<float>(NPVARIANT_TO_DOUBLE(variant)); + return true; + } else if (NPVARIANT_IS_INT32(variant)) { + *value = static_cast<float>(NPVARIANT_TO_INT32(variant)); + return true; + } + + return false; +} + +bool NPVariantToValue(double* value, const NPVariant& variant) { + if (NPVARIANT_IS_DOUBLE(variant)) { + *value = NPVARIANT_TO_DOUBLE(variant); + return true; + } else if (NPVARIANT_IS_INT32(variant)) { + *value = NPVARIANT_TO_INT32(variant); + return true; + } + + return false; +} + +bool NPVariantToValue(std::string* value, const NPVariant& variant) { + if (NPVARIANT_IS_STRING(variant)) { + const NPString& str = NPVARIANT_TO_STRING(variant); + *value = std::string(str.UTF8Characters, str.UTF8Length); + return true; + } + + return false; +} + +void ValueToNPVariant(bool value, NPVariant* variant) { + BOOLEAN_TO_NPVARIANT(value, *variant); +} + +void ValueToNPVariant(int32 value, NPVariant* variant) { + INT32_TO_NPVARIANT(value, *variant); +} + +void ValueToNPVariant(float value, NPVariant* variant) { + DOUBLE_TO_NPVARIANT(value, *variant); +} + +void ValueToNPVariant(double value, NPVariant* variant) { + DOUBLE_TO_NPVARIANT(value, *variant); +} + +void ValueToNPVariant(const std::string& value, NPVariant* variant) { + NPUTF8* p = static_cast<NPUTF8*>(NPBrowser::get()->MemAlloc(value.length())); + memcpy(p, value.c_str(), value.length()); + STRINGN_TO_NPVARIANT(p, value.length(), *variant); +} + +SmartNPVariant::SmartNPVariant() { + VOID_TO_NPVARIANT(*this); +} + +SmartNPVariant::SmartNPVariant(const SmartNPVariant& rhs) { + rhs.CopyTo(this); +} + +SmartNPVariant::SmartNPVariant(const NPVariant& rhs) { + static_cast<const SmartNPVariant&>(rhs).CopyTo(this); +} + +SmartNPVariant::~SmartNPVariant() { + Release(); +} + +SmartNPVariant& SmartNPVariant::operator=(const SmartNPVariant& rhs) { + Release(); + rhs.CopyTo(this); + return *this; +} + +SmartNPVariant& SmartNPVariant::operator=(const NPVariant& rhs) { + Release(); + static_cast<const SmartNPVariant&>(rhs).CopyTo(this); + return *this; +} + +bool SmartNPVariant::IsVoid() const { + return NPVARIANT_IS_VOID(*this); +} + +void SmartNPVariant::Release() { + NPBrowser::get()->ReleaseVariantValue(this); + VOID_TO_NPVARIANT(*this); +} + +void SmartNPVariant::Invalidate() { + if (NPVARIANT_IS_OBJECT(*this)) { + NULL_TO_NPVARIANT(*this); + } +} + +void SmartNPVariant::CopyTo(NPVariant* rhs) const { + if (NPVARIANT_IS_OBJECT(*this)) { + NPObject* object = NPVARIANT_TO_OBJECT(*this); + OBJECT_TO_NPVARIANT(object, *rhs); + NPBrowser::get()->RetainObject(object); + } else if (NPVARIANT_IS_STRING(*this)) { + NPUTF8* copy = static_cast<NPUTF8*>(NPBrowser::get()->MemAlloc( + value.stringValue.UTF8Length)); + memcpy(copy, + value.stringValue.UTF8Characters, + value.stringValue.UTF8Length); + STRINGN_TO_NPVARIANT(copy, value.stringValue.UTF8Length, *rhs); + } else { + memcpy(rhs, this, sizeof(*rhs)); + } +} + +bool NPHasMethod(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name) { + return NPBrowser::get()->HasMethod( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name)); +} + +bool NPHasProperty(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name) { + return NPBrowser::get()->HasProperty( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name)); +} + +bool NPRemoveProperty(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name) { + return NPBrowser::get()->RemoveProperty( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name)); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/np_utils.h b/o3d/gpu/np_utils/np_utils.h new file mode 100644 index 0000000..2ab9384 --- /dev/null +++ b/o3d/gpu/np_utils/np_utils.h @@ -0,0 +1,271 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_NP_UTILS_H_ +#define GPU_NP_UTILS_NP_UTILS_H_ + +#include <string> + +#include "gpu/np_utils/np_browser.h" +#include "gpu/np_utils/np_class.h" +#include "gpu/np_utils/np_object_pointer.h" +#include "gpu/np_utils/np_headers.h" + +namespace gpu_plugin { + +// Convert NPVariant to C++ type. Returns whether the conversion was successful. +bool NPVariantToValue(bool* value, const NPVariant& variant); +bool NPVariantToValue(int32* value, const NPVariant& variant); +bool NPVariantToValue(float* value, const NPVariant& variant); +bool NPVariantToValue(double* value, const NPVariant& variant); +bool NPVariantToValue(std::string* value, const NPVariant& variant); + +template <typename T> +bool NPVariantToValue(NPObjectPointer<T>* value, + const NPVariant& variant) { + if (NPVARIANT_IS_NULL(variant)) { + *value = NPObjectPointer<T>(); + return true; + } else if (NPVARIANT_IS_OBJECT(variant)) { + NPObject* object = NPVARIANT_TO_OBJECT(variant); + if (object->_class == NPGetClass<T>()) { + *value = NPObjectPointer<T>(static_cast<T*>( + NPVARIANT_TO_OBJECT(variant))); + return true; + } + } + + return false; +} + +// Specialization for NPObject does not check for mismatched NPClass. +template <> +inline bool NPVariantToValue(NPObjectPointer<NPObject>* value, + const NPVariant& variant) { + if (NPVARIANT_IS_NULL(variant)) { + *value = NPObjectPointer<NPObject>(); + return true; + } else if (NPVARIANT_IS_OBJECT(variant)) { + *value = NPObjectPointer<NPObject>(NPVARIANT_TO_OBJECT(variant)); + return true; + } + + return false; +} + +// Convert C++ type to NPVariant. +void ValueToNPVariant(bool value, NPVariant* variant); +void ValueToNPVariant(int32 value, NPVariant* variant); +void ValueToNPVariant(float value, NPVariant* variant); +void ValueToNPVariant(double value, NPVariant* variant); +void ValueToNPVariant(const std::string& value, NPVariant* variant); + +template <typename T> +void ValueToNPVariant(const NPObjectPointer<T>& value, + NPVariant* variant) { + if (value.Get()) { + NPBrowser::get()->RetainObject(value.Get()); + OBJECT_TO_NPVARIANT(value.Get(), *variant); + } else { + NULL_TO_NPVARIANT(*variant); + } +} + +// NPVariant that automatically manages lifetime of string and object variants. +class SmartNPVariant : public NPVariant { + public: + SmartNPVariant(); + SmartNPVariant(const SmartNPVariant& rhs); + explicit SmartNPVariant(const NPVariant& rhs); + + template <typename T> + explicit SmartNPVariant(const T& v) { + ValueToNPVariant(v, this); + } + + ~SmartNPVariant(); + + SmartNPVariant& operator=(const SmartNPVariant& rhs); + SmartNPVariant& operator=(const NPVariant& rhs); + + template <typename T> + bool GetValue(T* v) const { + return NPVariantToValue(v, *this); + } + + bool IsVoid() const; + + template <typename T> + void SetValue(const T& v) { + Release(); + ValueToNPVariant(v, this); + } + + void CopyTo(NPVariant* target) const; + + // Sets the variant to void. + void Release(); + + // Called when an NPObject is invalidated to clear any references to other + // NPObjects. Does not release the object as it might no longer be valid. + void Invalidate(); +}; + +// These allow a method to be invoked with automatic conversion of C++ +// types to variants for arguments and return values. + +bool NPHasMethod(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name); + +inline bool NPInvokeVoid(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name) { + SmartNPVariant result; + return NPBrowser::get()->Invoke( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + NULL, 0, + &result); +} + +template<typename R> +bool NPInvoke(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name, + R* r) { + SmartNPVariant result; + if (NPBrowser::get()->Invoke( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + NULL, 0, + &result)) { + return result.GetValue(r); + } + return false; +} + +template<typename P0> +bool NPInvokeVoid(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name, + P0 p0) { + SmartNPVariant args[1]; + args[0].SetValue(p0); + SmartNPVariant result; + return NPBrowser::get()->Invoke( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + &args[0], 1, + &result); +} + +template<typename R, typename P0> +bool NPInvoke(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name, + P0 p0, + R* r) { + SmartNPVariant args[1]; + args[0].SetValue(p0); + SmartNPVariant result; + if (NPBrowser::get()->Invoke( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + &args[0], 1, + &result)) { + return result.GetValue(r); + } + return false; +} + +template<typename P0, typename P1> +bool NPInvokeVoid(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name, + P0 p0, P1 p1) { + SmartNPVariant args[2]; + args[0].SetValue(p0); + args[1].SetValue(p1); + SmartNPVariant result; + return NPBrowser::get()->Invoke( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + &args[0], 2, + &result); +} + +template<typename R, typename P0, typename P1> +bool NPInvoke(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name, + P0 p0, P1 p1, + R* r) { + SmartNPVariant args[2]; + args[0].SetValue(p0); + args[1].SetValue(p1); + SmartNPVariant result; + if (NPBrowser::get()->Invoke( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + &args[0], 2, + &result)) { + return result.GetValue(r); + } + return false; +} + +bool NPHasProperty(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name); + +template <typename T> +bool NPGetProperty(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name, + T* value) { + SmartNPVariant result; + if (NPBrowser::get()->GetProperty(npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + &result)) { + return result.GetValue(value); + } + return false; +} + +template <typename T> +bool NPSetProperty(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name, + const T& value) { + SmartNPVariant variant(value); + return NPBrowser::get()->SetProperty( + npp, + object.Get(), + NPBrowser::get()->GetStringIdentifier(name), + &variant); +} + +bool NPRemoveProperty(NPP npp, + const NPObjectPointer<NPObject>& object, + const NPUTF8* name); + +template <typename NPObjectType> +NPObjectPointer<NPObjectType> NPCreateObject(NPP npp) { + const NPClass* np_class = NPGetClass<NPObjectType>(); + NPObjectType* object = static_cast<NPObjectType*>( + NPBrowser::get()->CreateObject(npp, np_class)); + return NPObjectPointer<NPObjectType>::FromReturned(object); +} + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_NP_UTILS_H_ diff --git a/o3d/gpu/np_utils/np_utils_unittest.cc b/o3d/gpu/np_utils/np_utils_unittest.cc new file mode 100644 index 0000000..e481187 --- /dev/null +++ b/o3d/gpu/np_utils/np_utils_unittest.cc @@ -0,0 +1,424 @@ +// Copyright (c) 2006-2008 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 "gpu/np_utils/np_object_mock.h" +#include "gpu/np_utils/np_browser_stub.h" +#include "gpu/np_utils/np_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::DoAll; +using testing::MakeMatcher; +using testing::Matcher; +using testing::Pointee; +using testing::Return; +using testing::SetArgumentPointee; +using testing::StrictMock; + +namespace gpu_plugin { + +class NPUtilsTest : public testing::Test { + protected: + StubNPBrowser stub_browser_; + NPP_t npp_; + NPVariant variant_; +}; + +TEST_F(NPUtilsTest, TestBoolNPVariantToValue) { + bool v; + + BOOLEAN_TO_NPVARIANT(false, variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_FALSE(v); + + BOOLEAN_TO_NPVARIANT(true, variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_TRUE(v); + + INT32_TO_NPVARIANT(7, variant_); + EXPECT_FALSE(NPVariantToValue(&v, variant_)); +} + +TEST_F(NPUtilsTest, TestIntNPVariantToValue) { + INT32_TO_NPVARIANT(7, variant_); + + int v1; + EXPECT_TRUE(NPVariantToValue(&v1, variant_)); + EXPECT_EQ(7, v1); + + float v2; + EXPECT_TRUE(NPVariantToValue(&v2, variant_)); + EXPECT_EQ(7.0f, v2); + + double v3; + EXPECT_TRUE(NPVariantToValue(&v3, variant_)); + EXPECT_EQ(7.0, v3); + + BOOLEAN_TO_NPVARIANT(false, variant_); + EXPECT_FALSE(NPVariantToValue(&v1, variant_)); +} + +TEST_F(NPUtilsTest, TestFloatNPVariantToValue) { + float v; + + DOUBLE_TO_NPVARIANT(7.0, variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_EQ(7.0f, v); + + BOOLEAN_TO_NPVARIANT(false, variant_); + EXPECT_FALSE(NPVariantToValue(&v, variant_)); +} + +TEST_F(NPUtilsTest, TestDoubleNPVariantToValue) { + double v; + + DOUBLE_TO_NPVARIANT(7.0, variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_EQ(7.0, v); + + BOOLEAN_TO_NPVARIANT(false, variant_); + EXPECT_FALSE(NPVariantToValue(&v, variant_)); +} + +TEST_F(NPUtilsTest, TestStringNPVariantToValue) { + std::string v; + + STRINGZ_TO_NPVARIANT("hello", variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_EQ(std::string("hello"), v); + + BOOLEAN_TO_NPVARIANT(false, variant_); + EXPECT_FALSE(NPVariantToValue(&v, variant_)); +} + +TEST_F(NPUtilsTest, TestObjectNPVariantToValue) { + NPObjectPointer<NPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + NPObjectPointer<NPObject> v; + + OBJECT_TO_NPVARIANT(object.Get(), variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_EQ(object, v); + + BOOLEAN_TO_NPVARIANT(false, variant_); + EXPECT_FALSE(NPVariantToValue(&v, variant_)); +} + +TEST_F(NPUtilsTest, TestNullNPVariantToValue) { + NPObjectPointer<NPObject> v; + + NULL_TO_NPVARIANT(variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_TRUE(NPObjectPointer<NPObject>() == v); + + BOOLEAN_TO_NPVARIANT(false, variant_); + EXPECT_FALSE(NPVariantToValue(&v, variant_)); +} + +TEST_F(NPUtilsTest, TestDerivedObjectNPVariantToValue) { + NPObjectPointer<NPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + NPObjectPointer<StrictMock<MockNPObject> > v; + + OBJECT_TO_NPVARIANT(object.Get(), variant_); + EXPECT_TRUE(NPVariantToValue(&v, variant_)); + EXPECT_EQ(object, v); +} + +TEST_F(NPUtilsTest, + TestDerivedObjectNPVariantToValueFailsIfValueHasDifferentType) { + NPObjectPointer<NPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + NPObjectPointer<MockNPObject> v; + + OBJECT_TO_NPVARIANT(object.Get(), variant_); + EXPECT_FALSE(NPVariantToValue(&v, variant_)); +} + +TEST_F(NPUtilsTest, TestBoolValueToNPVariant) { + ValueToNPVariant(true, &variant_); + EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(variant_)); + EXPECT_TRUE(NPVARIANT_TO_BOOLEAN(variant_)); + + ValueToNPVariant(false, &variant_); + EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(variant_)); + EXPECT_FALSE(NPVARIANT_TO_BOOLEAN(variant_)); +} + +TEST_F(NPUtilsTest, TestIntValueToNPVariant) { + ValueToNPVariant(7, &variant_); + EXPECT_TRUE(NPVARIANT_IS_INT32(variant_)); + EXPECT_EQ(7, NPVARIANT_TO_INT32(variant_)); +} + +TEST_F(NPUtilsTest, TestFloatValueToNPVariant) { + ValueToNPVariant(7.0f, &variant_); + EXPECT_TRUE(NPVARIANT_IS_DOUBLE(variant_)); + EXPECT_EQ(7.0, NPVARIANT_TO_DOUBLE(variant_)); +} + +TEST_F(NPUtilsTest, TestDoubleValueToNPVariant) { + ValueToNPVariant(7.0, &variant_); + EXPECT_TRUE(NPVARIANT_IS_DOUBLE(variant_)); + EXPECT_EQ(7.0, NPVARIANT_TO_DOUBLE(variant_)); +} + +TEST_F(NPUtilsTest, TestStringValueToNPVariant) { + ValueToNPVariant(std::string("hello"), &variant_); + EXPECT_TRUE(NPVARIANT_IS_STRING(variant_)); + EXPECT_EQ(std::string("hello"), + std::string(variant_.value.stringValue.UTF8Characters, + variant_.value.stringValue.UTF8Length)); +} + +TEST_F(NPUtilsTest, TestObjectValueToNPVariant) { + NPObjectPointer<NPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + ValueToNPVariant(object, &variant_); + EXPECT_TRUE(NPVARIANT_IS_OBJECT(variant_)); + EXPECT_EQ(object.Get(), NPVARIANT_TO_OBJECT(variant_)); + + NPBrowser::get()->ReleaseVariantValue(&variant_); +} + +TEST_F(NPUtilsTest, TestNullValueToNPVariant) { + ValueToNPVariant(NPObjectPointer<NPObject>(), &variant_); + EXPECT_TRUE(NPVARIANT_IS_NULL(variant_)); +} + +TEST_F(NPUtilsTest, CanCopyObjectSmartVariant) { + NPObjectPointer<NPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + EXPECT_EQ(1, object->referenceCount); + { + SmartNPVariant v1(object); + EXPECT_EQ(2, object->referenceCount); + { + SmartNPVariant v2(v1); + EXPECT_EQ(3, object->referenceCount); + } + EXPECT_EQ(2, object->referenceCount); + } + EXPECT_EQ(1, object->referenceCount); +} + +TEST_F(NPUtilsTest, CanCopyStringSmartVariant) { + SmartNPVariant v1(std::string("hello")); + SmartNPVariant v2(v1); + std::string r; + EXPECT_TRUE(NPVariantToValue(&r, v2)); + EXPECT_EQ(std::string("hello"), r); + EXPECT_NE(v1.value.stringValue.UTF8Characters, + v2.value.stringValue.UTF8Characters); +} + +TEST_F(NPUtilsTest, CanReleaseSmartVariant) { + SmartNPVariant variant(std::string("hello")); + EXPECT_FALSE(variant.IsVoid()); + variant.Release(); + EXPECT_TRUE(variant.IsVoid()); +} + +template <typename T> +class VariantMatcher : public testing::MatcherInterface<const NPVariant&> { + public: + explicit VariantMatcher(const T& value) : value_(value) { + } + + virtual bool Matches(const NPVariant& variant) const { + T other_value; + return NPVariantToValue(&other_value, variant) && other_value == value_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "equals " << value_; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not equal " << value_; + } + + private: + T value_; +}; + +template <typename T> +Matcher<const NPVariant&> VariantMatches(const T& value) { + return MakeMatcher(new VariantMatcher<T>(value)); +} + +TEST_F(NPUtilsTest, CanDetermineIfObjectHasMethod) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + EXPECT_CALL(*object, HasMethod(name)) + .WillOnce(Return(true)); + + EXPECT_TRUE(NPHasMethod(NULL, object, "foo")); +} + +TEST_F(NPUtilsTest, CanInvokeVoidMethodWithNativeTypes) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + VOID_TO_NPVARIANT(variant_); + + EXPECT_CALL(*object, Invoke(name, Pointee(VariantMatches<int32>(7)), 1, _)) + .WillOnce(DoAll(SetArgumentPointee<3>(variant_), + Return(true))); + + EXPECT_TRUE(NPInvokeVoid(NULL, object, "foo", 7)); +} + +TEST_F(NPUtilsTest, InvokeVoidMethodCanFail) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + VOID_TO_NPVARIANT(variant_); + + EXPECT_CALL(*object, Invoke(name, Pointee(VariantMatches<int32>(7)), 1, _)) + .WillOnce(DoAll(SetArgumentPointee<3>(variant_), + Return(false))); + + EXPECT_FALSE(NPInvokeVoid(NULL, object, "foo", 7)); +} + +TEST_F(NPUtilsTest, CanInvokeNonVoidMethodWithNativeTypes) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + DOUBLE_TO_NPVARIANT(1.5, variant_); + + EXPECT_CALL(*object, Invoke(name, Pointee(VariantMatches<int32>(7)), 1, _)) + .WillOnce(DoAll(SetArgumentPointee<3>(variant_), + Return(true))); + + double r; + EXPECT_TRUE(NPInvoke(NULL, object, "foo", 7, &r)); + EXPECT_EQ(1.5, r); +} + +TEST_F(NPUtilsTest, InvokeNonVoidMethodCanFail) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + DOUBLE_TO_NPVARIANT(1.5, variant_); + + EXPECT_CALL(*object, Invoke(name, Pointee(VariantMatches<int32>(7)), 1, _)) + .WillOnce(DoAll(SetArgumentPointee<3>(variant_), + Return(false))); + + double r; + EXPECT_FALSE(NPInvoke(NULL, object, "foo", 7, &r)); +} + +TEST_F(NPUtilsTest, InvokeNonVoidMethodFailsIfResultIsIncompatible) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + DOUBLE_TO_NPVARIANT(1.5, variant_); + + EXPECT_CALL(*object, Invoke(name, Pointee(VariantMatches<int32>(7)), 1, _)) + .WillOnce(DoAll(SetArgumentPointee<3>(variant_), + Return(true))); + + int r; + EXPECT_FALSE(NPInvoke(NULL, object, "foo", 7, &r)); +} + +TEST_F(NPUtilsTest, CanDetermineIfObjectHasProperty) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + EXPECT_CALL(*object, HasProperty(name)) + .WillOnce(Return(true)); + + EXPECT_TRUE(NPHasProperty(NULL, object, "foo")); +} + +TEST_F(NPUtilsTest, CanGetPropertyValue) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + DOUBLE_TO_NPVARIANT(1.5, variant_); + + EXPECT_CALL(*object, GetProperty(name, _)) + .WillOnce(DoAll(SetArgumentPointee<1>(variant_), + Return(true))); + + double r; + EXPECT_TRUE(NPGetProperty(NULL, object, "foo", &r)); +} + +TEST_F(NPUtilsTest, NPGetPropertyReportsFailureIfResultTypeIsDifferent) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + DOUBLE_TO_NPVARIANT(1.5, variant_); + + EXPECT_CALL(*object, GetProperty(name, _)) + .WillOnce(DoAll(SetArgumentPointee<1>(variant_), + Return(true))); + + bool r; + EXPECT_FALSE(NPGetProperty(NULL, object, "foo", &r)); +} + +TEST_F(NPUtilsTest, NPGetPropertyReportsFailureFromGetProperty) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + EXPECT_CALL(*object, GetProperty(name, _)) + .WillOnce(Return(false)); + + double r; + EXPECT_FALSE(NPGetProperty(NULL, object, "foo", &r)); +} + +TEST_F(NPUtilsTest, CanSetPropertyValue) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + EXPECT_CALL(*object, SetProperty(name, Pointee(VariantMatches(1.5)))) + .WillOnce(Return(true)); + + EXPECT_TRUE(NPSetProperty(NULL, object, "foo", 1.5)); +} + +TEST_F(NPUtilsTest, NPSetPropertyReportsFailureFromSetProperty) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + EXPECT_CALL(*object, SetProperty(name, Pointee(VariantMatches(1.5)))) + .WillOnce(Return(false)); + + EXPECT_FALSE(NPSetProperty(NULL, object, "foo", 1.5)); +} + +TEST_F(NPUtilsTest, CanRemovePropertyValue) { + NPIdentifier name = NPBrowser::get()->GetStringIdentifier("foo"); + NPObjectPointer<MockNPObject> object = + NPCreateObject<StrictMock<MockNPObject> >(NULL); + + EXPECT_CALL(*object, RemoveProperty(name)) + .WillOnce(Return(true)); + + EXPECT_TRUE(NPRemoveProperty(NULL, object, "foo")); +} + +} // namespace gpu_plugin diff --git a/o3d/gpu/np_utils/webkit_browser.h b/o3d/gpu/np_utils/webkit_browser.h new file mode 100644 index 0000000..8afc167 --- /dev/null +++ b/o3d/gpu/np_utils/webkit_browser.h @@ -0,0 +1,117 @@ +// Copyright (c) 2006-2008 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. + +#ifndef GPU_NP_UTILS_WEBKIT_BROWSER_H_ +#define GPU_NP_UTILS_WEBKIT_BROWSER_H_ + +// TODO(apatrick): This does not belong in np_utils. np_utils should not be +// dependent on WebKit (and it isn't - that's why the member functions are +// inline). + +#include <stdlib.h> + +#include "gpu/np_utils/np_browser.h" +#include "WebKit/api/public/WebBindings.h" + +typedef struct _NPNetscapeFuncs NPNetscapeFuncs; +typedef struct _NPChromiumFuncs NPChromiumFuncs; + +namespace gpu_plugin { + +// This class implements NPBrowser for the WebKit WebBindings. +class WebKitBrowser : public NPBrowser { + public: + WebKitBrowser(): NPBrowser(NULL) { + } + + // Standard functions from NPNetscapeFuncs. + + virtual NPIdentifier GetStringIdentifier(const NPUTF8* name) { + return WebKit::WebBindings::getStringIdentifier(name); + } + + virtual void* MemAlloc(size_t size) { + return malloc(size); + } + + virtual void MemFree(void* p) { + free(p); + } + + virtual NPObject* CreateObject(NPP npp, const NPClass* cl) { + return WebKit::WebBindings::createObject(npp, const_cast<NPClass*>(cl)); + } + + virtual NPObject* RetainObject(NPObject* object) { + return WebKit::WebBindings::retainObject(object); + } + + virtual void ReleaseObject(NPObject* object) { + WebKit::WebBindings::releaseObject(object); + } + + virtual void ReleaseVariantValue(NPVariant* variant) { + WebKit::WebBindings::releaseVariantValue(variant); + } + + virtual bool HasProperty(NPP npp, + NPObject* object, + NPIdentifier name) { + return WebKit::WebBindings::hasProperty(npp, object, name); + } + + virtual bool GetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + NPVariant* result) { + return WebKit::WebBindings::getProperty(npp, object, name, result); + } + + virtual bool SetProperty(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* result) { + return WebKit::WebBindings::setProperty(npp, object, name, result); + } + + virtual bool RemoveProperty(NPP npp, + NPObject* object, + NPIdentifier name) { + return WebKit::WebBindings::removeProperty(npp, object, name); + } + + virtual bool HasMethod(NPP npp, + NPObject* object, + NPIdentifier name) { + return WebKit::WebBindings::hasMethod(npp, object, name); + } + + virtual bool Invoke(NPP npp, + NPObject* object, + NPIdentifier name, + const NPVariant* args, + uint32_t num_args, + NPVariant* result) { + return WebKit::WebBindings::invoke(npp, object, name, args, num_args, + result); + } + + virtual NPObject* GetWindowNPObject(NPP npp) { + NPObject* window; + if (NPERR_NO_ERROR == NPN_GetValue(npp, + NPNVWindowNPObject, + &window)) { + return window; + } else { + return NULL; + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(WebKitBrowser); +}; + +} // namespace gpu_plugin + +#endif // GPU_NP_UTILS_WEBKIT_BROWSER_H_ |