diff options
author | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
---|---|---|
committer | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
commit | 05b47f7a8c5451f858dc220df0e3a97542edace6 (patch) | |
tree | a2273d619f0625c9d44d40842845ccce2eac1045 /o3d/command_buffer/service/cross | |
parent | 5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff) | |
download | chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2 |
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't
yet build in it's new home. We'll change that shortly.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/command_buffer/service/cross')
35 files changed, 8427 insertions, 0 deletions
diff --git a/o3d/command_buffer/service/cross/big_test.cc b/o3d/command_buffer/service/cross/big_test.cc new file mode 100644 index 0000000..b68a9da --- /dev/null +++ b/o3d/command_buffer/service/cross/big_test.cc @@ -0,0 +1,106 @@ +/* + * 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 "big" test of the whole command buffer service, making +// sure all the pieces fit together. +// +// Currently this checks that the RPC mechanism properly forwards call to the +// service thread. + +#include <build/build_config.h> +#include "command_buffer/common/cross/rpc_imc.h" +#include "command_buffer/service/cross/big_test_helpers.h" +#include "command_buffer/service/cross/buffer_rpc.h" +#include "command_buffer/service/cross/cmd_buffer_engine.h" +#include "command_buffer/service/cross/gapi_decoder.h" +#include "native_client/service_runtime/nrd_xfer_lib/nrd_all_modules.h" + +namespace o3d { +namespace command_buffer { + +nacl::SocketAddress g_address = { "command-buffer" }; + +// Main function. Creates a socket, and waits for an incoming connection on it. +// Then run the engine main loop. +void BigTest() { + NaClNrdAllModulesInit(); + GAPIDecoder decoder(g_gapi); + CommandBufferEngine engine(&decoder); + decoder.set_engine(&engine); + nacl::Handle server_socket = nacl::BoundSocket(&g_address); + nacl::Handle handles[1]; + nacl::MessageHeader msg; + msg.iov = NULL; + msg.iov_length = 0; + msg.handles = handles; + msg.handle_count = 1; + int r = nacl::ReceiveDatagram(server_socket, &msg, 0); + DCHECK_NE(r, -1); + nacl::Close(server_socket); + + nacl::HtpHandle htp_handle = nacl::CreateImcDesc(handles[0]); + IMCMessageProcessor processor(htp_handle, engine.rpc_impl()); + engine.set_process_interface(&processor); + IMCSender sender(htp_handle); + engine.set_client_rpc(&sender); + + bool result = g_gapi->Initialize(); + DCHECK(result); + + bool running = true; + while (running) { + running = ProcessSystemMessages(); + if (!running) break; + // DoWork() will block if there is nothing to be done, meaning we are only + // going to handle message after commands are sent. It should happen at + // least once a frame, so it's "good enough". + // TODO: figure out a way to wait on the socket OR messages with + // MsgWaitForMultipleObjects. Asynchronous ("overlapped") read on the + // socket may let us do that on windows. + running = engine.DoWork(); + } + g_gapi->Destroy(); + nacl::Close(htp_handle); + NaClNrdAllModulesFini(); +} + +} // namespace command_buffer +} // namespace o3d + +#ifdef OS_WIN +int big_test_main(int argc, wchar_t **argv) { +#else +int big_test_main(int argc, char **argv) { +#endif + o3d::command_buffer::BigTest(); + return 0; +} diff --git a/o3d/command_buffer/service/cross/big_test_helpers.h b/o3d/command_buffer/service/cross/big_test_helpers.h new file mode 100644 index 0000000..b32a577 --- /dev/null +++ b/o3d/command_buffer/service/cross/big_test_helpers.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 contains a few helper functions for running big tests. + +#ifndef O3D_COMMAND_BUFFER_SERVICE_CROSS_BIG_TEST_HELPERS_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_BIG_TEST_HELPERS_H__ + +#include <build/build_config.h> +#include "core/cross/types.h" +#include "command_buffer/common/cross/gapi_interface.h" + +namespace o3d { +namespace command_buffer { + +extern String *g_program_path; +extern GAPIInterface *g_gapi; + +// very simple thread API (hides platform-specific implementations). +typedef void (* ThreadFunc)(void *param); +class Thread; + +// Creates and starts a thread. +Thread *CreateThread(ThreadFunc func, void* param); + +// Joins (waits for) a thread, destroying it. +void JoinThread(Thread *thread); + +// Processes system messages. Should be called once a frame at least. +bool ProcessSystemMessages(); + +} // namespace command_buffer +} // namespace o3d + +#ifdef OS_WIN +int big_test_main(int argc, wchar_t **argv); +#else +int big_test_main(int argc, char **argv); +#endif + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_BIG_TEST_HELPERS_H__ diff --git a/o3d/command_buffer/service/cross/buffer_rpc.cc b/o3d/command_buffer/service/cross/buffer_rpc.cc new file mode 100644 index 0000000..c09f526 --- /dev/null +++ b/o3d/command_buffer/service/cross/buffer_rpc.cc @@ -0,0 +1,136 @@ +/* + * 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 implementation of the Command Buffer Synchronous API RPC +// glue. + +#include "command_buffer/common/cross/logging.h" +#include "command_buffer/service/cross/buffer_rpc.h" + +namespace o3d { +namespace command_buffer { + +// Implements the dispatch function, deciding on which function to call based +// on the message ID, taking the arguments trivially serialized in the data +// payload. +BufferRPCImpl::ReturnValue BufferRPCImpl::DoCall(int message_id, + const void *data, + size_t size, + RPCHandle const *handles, + size_t handle_count) { + switch (message_id) { + case INIT_CONNECTION: { + DCHECK_EQ(0, size); + DCHECK_EQ(0, handle_count); + handler_->InitConnection(); + return 0; + } + case CLOSE_CONNECTION: + DCHECK_EQ(0, size); + DCHECK_EQ(0, handle_count); + handler_->CloseConnection(); + return 0; + case REGISTER_SHARED_MEMORY: { + DCHECK_EQ(sizeof(size_t), size); // NOLINT + DCHECK_EQ(1, handle_count); + RPCShmHandle buffer = static_cast<RPCShmHandle>(handles[0]); + return handler_->RegisterSharedMemory(buffer, + *static_cast<const size_t *>(data)); + } + case UNREGISTER_SHARED_MEMORY: { + DCHECK_EQ(sizeof(unsigned int), size); // NOLINT + DCHECK_EQ(0, handle_count); + unsigned int shm_id = *(static_cast<const unsigned int *>(data)); + handler_->UnregisterSharedMemory(shm_id); + return 0; + } + case SET_COMMAND_BUFFER: { + DCHECK_EQ(sizeof(SetCommandBufferStruct), size); + DCHECK_EQ(0, handle_count); + const SetCommandBufferStruct *params = + static_cast<const SetCommandBufferStruct *>(data); + handler_->SetCommandBuffer(params->shm_id, + params->offset, + params->size, + params->start_get); + return 0; + } + case PUT: { + DCHECK_EQ(sizeof(CommandBufferOffset), size); + DCHECK_EQ(0, handle_count); + CommandBufferOffset offset = + *(static_cast<const CommandBufferOffset *>(data)); + handler_->Put(offset); + return 0; + } + case GET: + DCHECK_EQ(0, size); + DCHECK_EQ(0, handle_count); + return handler_->Get(); + case GET_TOKEN: + DCHECK_EQ(0, size); + DCHECK_EQ(0, handle_count); + return handler_->GetToken(); + case WAIT_GET_CHANGES: { + DCHECK_EQ(sizeof(CommandBufferOffset), size); + DCHECK_EQ(0, handle_count); + CommandBufferOffset current_value = + *(static_cast<const CommandBufferOffset *>(data)); + return handler_->WaitGetChanges(current_value); + } + case SIGNAL_GET_CHANGES: { + DCHECK_EQ(sizeof(SignalGetChangesStruct), size); + DCHECK_EQ(0, handle_count); + const SignalGetChangesStruct *params = + static_cast<const SignalGetChangesStruct *>(data); + handler_->SignalGetChanges(params->current_value, + params->rpc_message_id); + return 0; + } + case GET_STATUS: { + DCHECK_EQ(0, size); + DCHECK_EQ(0, handle_count); + return handler_->GetStatus(); + } + case GET_PARSE_ERROR: { + DCHECK_EQ(0, size); + DCHECK_EQ(0, handle_count); + return handler_->GetParseError(); + } + default: + LOG(FATAL) << "unsupported RPC"; + return 0; + } +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/buffer_rpc.h b/o3d/command_buffer/service/cross/buffer_rpc.h new file mode 100644 index 0000000..f49dc0d --- /dev/null +++ b/o3d/command_buffer/service/cross/buffer_rpc.h @@ -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 defines the RPC glue for the Command Buffer Synchronous API, +// service side: an implementation of a RPC service, forwarding calls to +// a Command Buffer API implemention (RPC -> API). + +#ifndef O3D_COMMAND_BUFFER_SERVICE_CROSS_BUFFER_RPC_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_BUFFER_RPC_H__ + +#include "command_buffer/common/cross/buffer_sync_api.h" +#include "command_buffer/common/cross/rpc.h" + +namespace o3d { +namespace command_buffer { + +// RPC service implementation, implementing the Command Buffer Synchronous API +// RPC glue. This class is temporary, and will be replaced when the +// NativeClient RPC mechanism will be available. +// +// The API exposed through the RPC mechanism maps 1-to-1 to BufferSyncInterface, +// trivially serializing arguments. +class BufferRPCImpl: public RPCImplInterface { + public: + explicit BufferRPCImpl(BufferSyncInterface *handler) : handler_(handler) {} + virtual ~BufferRPCImpl() {} + + enum MessageId { + INIT_CONNECTION = RESPONSE_ID + 1, + CLOSE_CONNECTION, + REGISTER_SHARED_MEMORY, + UNREGISTER_SHARED_MEMORY, + SET_COMMAND_BUFFER, + PUT, + GET, + GET_TOKEN, + WAIT_GET_CHANGES, + SIGNAL_GET_CHANGES, + GET_STATUS, + GET_PARSE_ERROR, + }; + + // Structure describing the arguments for the SET_COMMAND_BUFFER RPC. + struct SetCommandBufferStruct { + unsigned int shm_id; + ptrdiff_t offset; + size_t size; + CommandBufferOffset start_get; + }; + + // Structure describing the arguments for the SIGNAL_GET_CHANGES RPC. + struct SignalGetChangesStruct { + CommandBufferOffset current_value; + int rpc_message_id; + }; + + // Implements the DoCall interface, interpreting the message with the + // parameters, and passing the calls with arguments to the handler. + virtual ReturnValue DoCall(int message_id, + const void * data, + size_t size, + RPCHandle const *handles, + size_t handle_count); + + private: + BufferSyncInterface *handler_; +}; + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_BUFFER_RPC_H__ diff --git a/o3d/command_buffer/service/cross/buffer_rpc_test.cc b/o3d/command_buffer/service/cross/buffer_rpc_test.cc new file mode 100644 index 0000000..42122b9 --- /dev/null +++ b/o3d/command_buffer/service/cross/buffer_rpc_test.cc @@ -0,0 +1,171 @@ +/* + * 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 RPC glue. + +#include "tests/common/win/testing_common.h" +#include "command_buffer/common/cross/mocks.h" +#include "command_buffer/service/cross/buffer_rpc.h" +#include "command_buffer/service/cross/mocks.h" + +namespace o3d { +namespace command_buffer { + +using testing::Return; + +// Test fixture for BufferRPCImpl test. Creates a BufferSyncMock and a +// BufferRPCImpl that uses it. +class BufferRPCImplTest : public testing::Test { + protected: + virtual void SetUp() { + buffer_sync_mock_.reset(new BufferSyncMock); + buffer_rpc_impl_.reset(new BufferRPCImpl(buffer_sync_mock_.get())); + } + virtual void TearDown() {} + + BufferSyncMock &buffer_sync_mock() { return *buffer_sync_mock_.get(); } + BufferRPCImpl *buffer_rpc_impl() { return buffer_rpc_impl_.get(); } + private: + scoped_ptr<BufferSyncMock> buffer_sync_mock_; + scoped_ptr<BufferRPCImpl> buffer_rpc_impl_; +}; + +// Checks that the INIT_CONNECTION RPC is properly parsed. +TEST_F(BufferRPCImplTest, TestInitConnection) { + EXPECT_CALL(buffer_sync_mock(), InitConnection()); + buffer_rpc_impl()->DoCall(BufferRPCImpl::INIT_CONNECTION, NULL, 0, NULL, 0); +} + +// Checks that the CLOSE_CONNECTION RPC is properly parsed. +TEST_F(BufferRPCImplTest, TestCloseConnection) { + EXPECT_CALL(buffer_sync_mock(), CloseConnection()); + buffer_rpc_impl()->DoCall(BufferRPCImpl::CLOSE_CONNECTION, NULL, 0, NULL, 0); +} + +// Checks that the REGISTER_SHARED_MEMORY RPC is properly parsed and that the +// return value is properly forwarded. +TEST_F(BufferRPCImplTest, TestRegisterSharedMemory) { + RPCShmHandle shm = reinterpret_cast<RPCShmHandle>(456); + size_t size = 789; + EXPECT_CALL(buffer_sync_mock(), RegisterSharedMemory(shm, size)) + .WillOnce(Return(1234)); + RPCHandle handles[1] = {shm}; + EXPECT_EQ(1234, buffer_rpc_impl()->DoCall( + BufferRPCImpl::REGISTER_SHARED_MEMORY, &size, sizeof(size), handles, 1)); +} + +// Checks that the UNREGISTER_SHARED_MEMORY RPC is properly parsed. +TEST_F(BufferRPCImplTest, TestUnregisterSharedMemory) { + unsigned int shm_id = 385; + EXPECT_CALL(buffer_sync_mock(), UnregisterSharedMemory(shm_id)); + buffer_rpc_impl()->DoCall(BufferRPCImpl::UNREGISTER_SHARED_MEMORY, &shm_id, + sizeof(shm_id), NULL, 0); +} + +// Checks that the SET_COMMAND_BUFFER RPC is properly parsed. +TEST_F(BufferRPCImplTest, TestSetCommandBuffer) { + EXPECT_CALL(buffer_sync_mock(), SetCommandBuffer(93, 7878, 3434, 5151)); + BufferRPCImpl::SetCommandBufferStruct param; + param.shm_id = 93; + param.offset = 7878; + param.size = 3434; + param.start_get = 5151; + buffer_rpc_impl()->DoCall(BufferRPCImpl::SET_COMMAND_BUFFER, ¶m, + sizeof(param), NULL, 0); +} + +// Checks that the PUT RPC is properly parsed. +TEST_F(BufferRPCImplTest, TestPut) { + CommandBufferOffset offset = 8765; + EXPECT_CALL(buffer_sync_mock(), Put(offset)); + buffer_rpc_impl()->DoCall(BufferRPCImpl::PUT, &offset, sizeof(offset), NULL, + 0); +} + +// Checks that the GET RPC is properly parsed and that the return value is +// properly forwarded. +TEST_F(BufferRPCImplTest, TestGet) { + EXPECT_CALL(buffer_sync_mock(), Get()).WillOnce(Return(9375)); + EXPECT_EQ(9375, buffer_rpc_impl()->DoCall(BufferRPCImpl::GET, NULL, 0, NULL, + 0)); +} + +// Checks that the GET_TOKEN RPC is properly parsed and that the return value is +// properly forwarded. +TEST_F(BufferRPCImplTest, TestGetToken) { + EXPECT_CALL(buffer_sync_mock(), GetToken()).WillOnce(Return(1618)); + EXPECT_EQ(1618, buffer_rpc_impl()->DoCall(BufferRPCImpl::GET_TOKEN, NULL, 0, + NULL, 0)); +} + +// Checks that the WAIT_GET_CHANGES RPC is properly parsed and that the return +// value is properly forwarded. +TEST_F(BufferRPCImplTest, TestWaitGetChanges) { + CommandBufferOffset value = 339; + EXPECT_CALL(buffer_sync_mock(), WaitGetChanges(value)) + .WillOnce(Return(16180)); + EXPECT_EQ(16180, buffer_rpc_impl()->DoCall(BufferRPCImpl::WAIT_GET_CHANGES, + &value, sizeof(value), NULL, 0)); +} + +// Checks that the SIGNAL_GET_CHANGES RPC is properly parsed. +TEST_F(BufferRPCImplTest, TestSignalGetChanges) { + EXPECT_CALL(buffer_sync_mock(), SignalGetChanges(34, 21)); + BufferRPCImpl::SignalGetChangesStruct param; + param.current_value = 34; + param.rpc_message_id = 21; + buffer_rpc_impl()->DoCall(BufferRPCImpl::SIGNAL_GET_CHANGES, ¶m, + sizeof(param), NULL, 0); +} + +// Checks that the GET_STATUS RPC is properly parsed and that the return value +// is properly forwarded. +TEST_F(BufferRPCImplTest, TestGetStatus) { + EXPECT_CALL(buffer_sync_mock(), GetStatus()) + .WillOnce(Return(BufferSyncInterface::PARSE_ERROR)); + EXPECT_EQ(BufferSyncInterface::PARSE_ERROR, + buffer_rpc_impl()->DoCall(BufferRPCImpl::GET_STATUS, NULL, 0, NULL, + 0)); +} + +// Checks that the GET_STATUS RPC is properly parsed and that the return value +// is properly forwarded. +TEST_F(BufferRPCImplTest, TestGetParseError) { + EXPECT_CALL(buffer_sync_mock(), GetParseError()) + .WillOnce(Return(BufferSyncInterface::PARSE_OUT_OF_BOUNDS)); + EXPECT_EQ(BufferSyncInterface::PARSE_OUT_OF_BOUNDS, + buffer_rpc_impl()->DoCall(BufferRPCImpl::GET_PARSE_ERROR, NULL, 0, + NULL, 0)); +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/cmd_buffer_engine.cc b/o3d/command_buffer/service/cross/cmd_buffer_engine.cc new file mode 100644 index 0000000..d96c63d --- /dev/null +++ b/o3d/command_buffer/service/cross/cmd_buffer_engine.cc @@ -0,0 +1,290 @@ +/* + * 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 engine. + +#include "command_buffer/service/cross/buffer_rpc.h" +#include "command_buffer/service/cross/cmd_buffer_engine.h" + +namespace o3d { +namespace command_buffer { + +// Creates a RPC implementation using 'this' as the handler, and a RPC server +// for it. +CommandBufferEngine::CommandBufferEngine(AsyncAPIInterface *handler) + : buffer_rpc_impl_(), + process_interface_(NULL), + parser_(), + handler_(handler), + client_rpc_(NULL), + token_(0), + status_(NOT_CONNECTED), + signal_change_(false), + signal_rpc_message_id_(0), + parse_error_(PARSE_NO_ERROR) { + buffer_rpc_impl_.reset(new BufferRPCImpl(this)); +} + +CommandBufferEngine::~CommandBufferEngine() {} + +// Inits the connection. Registers the client RPC service. +void CommandBufferEngine::InitConnection() { + status_ = NO_BUFFER; +} + +// Closes the connection. Executes all remaining commands. +void CommandBufferEngine::CloseConnection() { + FinishParsing(); + status_ = NOT_CONNECTED; + parser_.reset(NULL); +} + +// Adds the shared memory buffer somewhere into the list, return the index in +// the list as the handle. Either find a hole in the list, or add it at the +// end. We don't want to invalidate exiting indices. +unsigned int CommandBufferEngine::RegisterSharedMemory( + RPCShmHandle handle, + size_t size) { + void *address = MapShm(handle, size); + if (!address) return kInvalidSharedMemoryId; + MemoryMapping mapping = {address, size}; + for (unsigned int i = 0; i < shared_memory_buffers_.size(); ++i) { + if (shared_memory_buffers_[i].address == NULL) { + shared_memory_buffers_[i] = mapping; + return i; + } + } + shared_memory_buffers_.push_back(mapping); + return static_cast<unsigned int>(shared_memory_buffers_.size() - 1); +} + +// Sets the list entry for the shared memory buffer to NULL. Don't erase() the +// entry, We don't want to invalidate exiting indices. +void CommandBufferEngine::UnregisterSharedMemory(unsigned int shm_id) { + if ((shm_id >= shared_memory_buffers_.size()) || + !shared_memory_buffers_[shm_id].size) { + LOG(ERROR) << "Trying to unregister a non-registered shared memory"; + return; + } + MemoryMapping &mapping = shared_memory_buffers_[shm_id]; + UnmapShm(mapping.address, mapping.size); + mapping.address = NULL; + mapping.size = 0; +} + +// Sets the command buffer. Executes all remaining commands in the old buffer +// (if any) and creates a new command parser. +void CommandBufferEngine::SetCommandBuffer(unsigned int shm_id, + ptrdiff_t offset, + size_t size, + CommandBufferOffset start_get) { + if ((shm_id >= shared_memory_buffers_.size()) || + !shared_memory_buffers_[shm_id].size) { + LOG(ERROR) << "Trying to set the command buffer from a non-registered " + << "shared memory"; + return; + } + if (status_ == NOT_CONNECTED) return; + FinishParsing(); + parser_.reset(new CommandParser(shared_memory_buffers_[shm_id].address, + shared_memory_buffers_[shm_id].size, offset, + size, start_get, handler_)); + status_ = PARSING; + parse_error_ = PARSE_NO_ERROR; +} + +// Changes the put value. +void CommandBufferEngine::Put(CommandBufferOffset offset) { + if (parser_.get()) { + parser_->set_put(offset); + } +} + +// Retrieves the get value. This returns -1 if there is no current parser. +CommandBufferOffset CommandBufferEngine::Get() { + if (parser_.get()) { + return parser_->get(); + } else { + return -1; + } +} + +// Retrieves the current token value. +unsigned int CommandBufferEngine::GetToken() { + return token_; +} + +// Executes commands until get is different from the value passed in. It will +// return immediately if the get value is already different, or if the engine +// is not in the PARSING status, or if the buffer is empty. It will return -1 +// if there is no current buffer. +CommandBufferOffset CommandBufferEngine::WaitGetChanges( + CommandBufferOffset current_value) { + if (parser_.get()) { + while (status_ == PARSING && + parser_->get() == current_value && + !parser_->IsEmpty()) { + ProcessOneCommand(); + } + return parser_->get(); + } else { + return -1; + } +} + +// Signals the client when get gets different from the value passed in. If get +// is already different, or if the engine is not in the PARSING status, that +// will happen immediately, otherwise it will happen when commands get +// executed, moving the get pointer. +void CommandBufferEngine::SignalGetChanges(CommandBufferOffset current_value, + int rpc_message_id) { + if (status_ != PARSING || parser_->get() != current_value) { + DoSignalChangedGet(rpc_message_id); + } else { + signal_change_ = true; + signal_rpc_message_id_ = rpc_message_id; + } +} + +// Gets the memory address from the list entry. +void *CommandBufferEngine::GetSharedMemoryAddress(unsigned int shm_id) { + if ((shm_id >= shared_memory_buffers_.size()) || + !shared_memory_buffers_[shm_id].size) { + LOG(ERROR) << "Trying to get the address of a non-registered shared memory"; + return NULL; + } + return shared_memory_buffers_[shm_id].address; +} + +// Gets the memory size from the list entry. +size_t CommandBufferEngine::GetSharedMemorySize(unsigned int shm_id) { + if ((shm_id >= shared_memory_buffers_.size()) || + !shared_memory_buffers_[shm_id].size) { + LOG(ERROR) << "Trying to get the size of a non-registered shared memory"; + return 0; + } + return shared_memory_buffers_[shm_id].size; +} + +// Gets the status. +BufferSyncInterface::ParserStatus CommandBufferEngine::GetStatus() { + return status_; +} + +// Gets the current parse error, reset it to PARSE_NO_ERROR. +BufferSyncInterface::ParseError CommandBufferEngine::GetParseError() { + ParseError error = parse_error_; + parse_error_ = PARSE_NO_ERROR; + return error; +} + +// Finishes parsing, executing all the commands until the buffer is empty, or a +// parsing error occurs. +void CommandBufferEngine::FinishParsing() { + // terminates current parsing, that is, execute all the commands + // NOTE: status_ == PARSING implies parser_ != NULL + while (status_ == PARSING && !parser_->IsEmpty()) { + ProcessOneCommand(); + } +} + +// Processes one command from the command buffer. This must only be called when +// in the PARSING status. +// This will update the status_ and the parse_error_ fields if an error occurs. +void CommandBufferEngine::ProcessOneCommand() { + DCHECK_EQ(PARSING, status_); + DCHECK(parser_.get()); + ParseError result = parser_->ProcessCommand(); + switch (result) { + case PARSE_NO_ERROR: + break; + case PARSE_OUT_OF_BOUNDS: + case PARSE_INVALID_SIZE: + status_ = PARSE_ERROR; + // Always override the error, to properly signal the stopping condition. + parse_error_ = result; + break; + case PARSE_INVALID_ARGUMENTS: + case PARSE_UNKNOWN_COMMAND: + // Only set the error if it is not set already. + if (parse_error_ == PARSE_NO_ERROR) { + parse_error_ = result; + } + break; + } + // get has changed, signal the client if needed. + if (signal_change_) { + DoSignalChangedGet(signal_rpc_message_id_); + signal_change_ = false; + } +} + +// Executes the main loop. While there are commands in the buffer, processes +// them one by one, checking for RPC messages between each of them (executing +// all of them). If the buffer is empty, block until a RPC message comes. +void CommandBufferEngine::DoMainLoop() { + while (DoWork()) { } + // Clean up if needed: execute all pending commands, then close the + // connection. + if (status_ != NOT_CONNECTED) CloseConnection(); +} + +bool CommandBufferEngine::HasWork() { + return (status_ == PARSING && !parser_->IsEmpty()) || + process_interface_->HasMessage(); +} + +bool CommandBufferEngine::DoWork() { + if (status_ == PARSING && !parser_->IsEmpty()) { + bool running = true; + // process as many messages as available but do not block. + while (process_interface_->HasMessage()) { + running = process_interface_->ProcessMessage(); + } + if (running) ProcessOneCommand(); + return running; + } else { + // call ProcessMessage, always blocking. We have nothing else to do. + return process_interface_->ProcessMessage(); + } +} + +// Signals that get has changed, sending a RPC message back to the client. It +// will send -1 if there is no current buffer. +void CommandBufferEngine::DoSignalChangedGet(int rpc_message_id) { + DCHECK(client_rpc_); + CommandBufferOffset get = parser_.get() ? parser_->get() : -1; + client_rpc_->SendCall(rpc_message_id, &get, sizeof(get), NULL, 0); +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/cmd_buffer_engine.h b/o3d/command_buffer/service/cross/cmd_buffer_engine.h new file mode 100644 index 0000000..66cab72 --- /dev/null +++ b/o3d/command_buffer/service/cross/cmd_buffer_engine.h @@ -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 defines the CommandBufferEngine class, providing the main loop for +// the service, exposing the RPC API, managing the command parser. + +#ifndef O3D_COMMAND_BUFFER_SERVICE_CROSS_CMD_BUFFER_ENGINE_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_CMD_BUFFER_ENGINE_H__ + +#include <vector> +#include "base/scoped_ptr.h" +#include "command_buffer/common/cross/buffer_sync_api.h" +#include "command_buffer/service/cross/cmd_parser.h" + +namespace o3d { +namespace command_buffer { + +class BufferRPCImpl; + +class CommandBufferEngine : public BufferSyncInterface { + public: + explicit CommandBufferEngine(AsyncAPIInterface *handler); + virtual ~CommandBufferEngine(); + + // Initializes the connection with the client. + virtual void InitConnection(); + + // Closes the connection with the client. + virtual void CloseConnection(); + + // Registers a shared memory buffer. While a buffer is registered, it can be + // accessed by the service, including the underlying asynchronous API, + // through a single identifier. + // Parameters: + // buffer: the shared memory buffer handle. + // Returns: + // an identifier for the shared memory. + virtual unsigned int RegisterSharedMemory(RPCShmHandle buffer, size_t size); + + // Unregisters a shared memory buffer. + // Parameters: + // shm_id: the identifier for the shared memory buffer. + virtual void UnregisterSharedMemory(unsigned int shm_id); + + // Initializes the command buffer. + // Parameters: + // buffer: the memory buffer descriptor in which the command buffer + // resides. + // offset: the offset of the command buffer, relative to the memory + // buffer. + // size: the size of the command buffer. + // start_get: the inital value for the Get pointer, relative to the + // command buffer, where the parser will start interpreting + // commands. Put is also initialized to that value. + virtual void SetCommandBuffer(unsigned int shm_id, + ptrdiff_t offset, + size_t size, + CommandBufferOffset start_get); + + // Sets the value of the Put pointer. + // Parameters: + // offset: the new value of the Put pointer, as an offset into the command + // buffer. + virtual void Put(CommandBufferOffset offset); + + // Gets the value of the Get pointer. + // Returns: + // the current value of the Get pointer, as an offset into the command + // buffer. + virtual CommandBufferOffset Get(); + + // Gets the current token value. + // Returns: + // the current token value. + virtual unsigned int GetToken(); + + // Waits until Get changes from the currently known value. + // Parameters: + // current_value: the currently known value. This call will block until + // Get is different from that value (returning immediately + // if it is already different). + // Returns: + // the current (changed) value of Get. + virtual CommandBufferOffset WaitGetChanges( + CommandBufferOffset current_value); + + // Asks the service to signal the client when Get changes from the currently + // known value. This is a non-blocking version of WaitGetChanges. + // Parameters: + // current_value: the currently known value of Get. + // rpc_message_id: the RPC message ID to call on the client when Get is + // different from current_value. That RPC is called + // immediately if Get is already different from + // current_value. + virtual void SignalGetChanges(CommandBufferOffset current_value, + int rpc_message_id); + + // Gets the status of the service. + // Returns: + // The status of the service. + virtual ParserStatus GetStatus(); + + // Gets the current parse error. The current parse error is set when the + // service is in the PARSE_ERROR status. It may also be set while in the + // PARSING state, if a recoverable error (like PARSE_UNKNOWN_METHOD) was + // encountered. Getting the error resets it to PARSE_NO_ERROR. + // Returns: + // The current parse error. + virtual ParseError GetParseError(); + + // Gets the base address of a registered shared memory buffer. + // Parameters: + // shm_id: the identifier for the shared memory buffer. + void *GetSharedMemoryAddress(unsigned int shm_id); + + // Gets the size of a registered shared memory buffer. + // Parameters: + // shm_id: the identifier for the shared memory buffer. + size_t GetSharedMemorySize(unsigned int shm_id); + + // Executes the main loop: parse commands and execute RPC calls until the + // server is killed. + void DoMainLoop(); + + // Returns whether or not the engine has work to do (process synchronous or + // asynchronous commands). + bool HasWork(); + + // Does some work (process synchronous or asynchronous commands). It will not + // block if HasWork() returns true. + // Returns: + // true if the engine should keep running, false if it has been sent a + // command to terminate. + bool DoWork(); + + // Gets the RPC server. + BufferRPCImpl *rpc_impl() const { return buffer_rpc_impl_.get(); } + + // Sets the RPC processing interface. + void set_process_interface(RPCProcessInterface *iface) { + process_interface_ = iface; + } + + // Sets the RPC processing interface. + void set_client_rpc(RPCSendInterface *iface) { + client_rpc_ = iface; + } + + // Gets the command parser. + CommandParser *parser() const { return parser_.get(); } + + // Sets the token value. + void set_token(unsigned int token) { token_ = token; } + private: + + // Processes one command from the command buffer. + void ProcessOneCommand(); + + // Sends the signal that get has changed to the client. + void DoSignalChangedGet(int rpc_message_id); + + // Finish parsing and executing all the commands in the buffer. + void FinishParsing(); + + scoped_ptr<BufferRPCImpl> buffer_rpc_impl_; + RPCProcessInterface *process_interface_; + scoped_ptr<CommandParser> parser_; + AsyncAPIInterface *handler_; + RPCSendInterface *client_rpc_; + unsigned int token_; + ParserStatus status_; + bool signal_change_; + int signal_rpc_message_id_; + ParseError parse_error_; + struct MemoryMapping { + void *address; + size_t size; + }; + std::vector<MemoryMapping> shared_memory_buffers_; +}; + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_CMD_BUFFER_ENGINE_H__ diff --git a/o3d/command_buffer/service/cross/cmd_buffer_engine_test.cc b/o3d/command_buffer/service/cross/cmd_buffer_engine_test.cc new file mode 100644 index 0000000..14f56ec --- /dev/null +++ b/o3d/command_buffer/service/cross/cmd_buffer_engine_test.cc @@ -0,0 +1,626 @@ +/* + * 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 Engine. + +#include "tests/common/win/testing_common.h" +#include "command_buffer/service/cross/cmd_buffer_engine.h" +#include "command_buffer/service/cross/mocks.h" + +namespace o3d { +namespace command_buffer { + +using testing::AnyNumber; +using testing::Return; +using testing::Mock; +using testing::Truly; +using testing::Sequence; +using testing::_; + +// Test fixture for CommandBufferEngine test - Creates a CommandBufferEngine, +// using a mock AsyncAPIInterface. +class CommandBufferEngineTest : public testing::Test { + protected: + CommandBufferEngineTest() + : shm_(kRPCInvalidHandle), + shm_id_(BufferSyncInterface::kInvalidSharedMemoryId) {} + + virtual void SetUp() { + api_mock_.reset(new AsyncAPIMock); + engine_.reset(new CommandBufferEngine(api_mock_.get())); + process_mock_.reset(new RPCProcessMock()); + engine_->set_process_interface(process_mock_.get()); + } + virtual void TearDown() { + } + + // Creates a shared memory buffer for the command buffer, and pass it to the + // engine. + CommandBufferEntry *InitCommandBuffer(size_t entries, unsigned int start) { + CHECK(shm_ == kRPCInvalidHandle); + CHECK(shm_id_ == BufferSyncInterface::kInvalidSharedMemoryId); + const size_t kShmSize = entries * sizeof(CommandBufferEntry); // NOLINT + shm_ = CreateShm(kShmSize); + EXPECT_NE(kRPCInvalidHandle, shm_); + if (kRPCInvalidHandle == shm_) return NULL; + shm_id_ = engine()->RegisterSharedMemory(shm_, kShmSize); + EXPECT_NE(BufferSyncInterface::kInvalidSharedMemoryId, shm_id_); + if (shm_id_ == BufferSyncInterface::kInvalidSharedMemoryId) { + DestroyShm(shm_); + shm_ = kRPCInvalidHandle; + return NULL; + } + engine()->SetCommandBuffer(shm_id_, 0, kShmSize, start); + return static_cast<CommandBufferEntry *>( + engine()->GetSharedMemoryAddress(shm_id_)); + } + + // Destroys the command buffer. + void DestroyCommandBuffer() { + engine()->UnregisterSharedMemory(shm_id_); + shm_id_ = BufferSyncInterface::kInvalidSharedMemoryId; + DestroyShm(shm_); + shm_ = kRPCInvalidHandle; + } + + // Adds a command to the buffer, while adding it as an expected call on the + // API mock. + unsigned int AddCommandWithExpect(CommandBufferEntry *buffer, + BufferSyncInterface::ParseError _return, + unsigned int command, + unsigned int arg_count, + CommandBufferEntry *args) { + unsigned int put = 0; + CommandHeader header; + header.size = arg_count + 1; + header.command = command; + buffer[put++].value_header = header; + for (unsigned int i = 0; i < arg_count; ++i) { + buffer[put++].value_uint32 = args[i].value_uint32; + } + EXPECT_CALL(*api_mock(), DoCommand(command, arg_count, + Truly(AsyncAPIMock::IsArgs(arg_count, args)))) + .InSequence(sequence_) + .WillOnce(Return(_return)); + return put; + } + + CommandBufferEngine *engine() { return engine_.get(); } + RPCProcessMock *process_mock() { return process_mock_.get(); } + AsyncAPIMock *api_mock() { return api_mock_.get(); } + private: + scoped_ptr<AsyncAPIMock> api_mock_; + scoped_ptr<CommandBufferEngine> engine_; + scoped_ptr<RPCProcessMock> process_mock_; + // handles for the command buffer. + RPCShmHandle shm_; + unsigned int shm_id_; + Sequence sequence_; +}; + +// Tests the initialization/termination sequence, checking that it sets the +// engine in the correct states. +TEST_F(CommandBufferEngineTest, TestInitialization) { + // Check initial state + EXPECT_TRUE(engine()->rpc_impl() != NULL); + EXPECT_TRUE(engine()->parser() == NULL); + EXPECT_EQ(BufferSyncInterface::NOT_CONNECTED, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + EXPECT_EQ(-1, engine()->Get()); + EXPECT_EQ(0, engine()->GetToken()); + + engine()->InitConnection(); + EXPECT_EQ(BufferSyncInterface::NO_BUFFER, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + EXPECT_EQ(-1, engine()->Get()); + + CommandBufferEntry *entries = InitCommandBuffer(25, 5); + ASSERT_TRUE(entries != NULL); + + EXPECT_EQ(BufferSyncInterface::PARSING, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + EXPECT_EQ(5, engine()->Get()); + EXPECT_TRUE(engine()->parser() != NULL); + + engine()->set_token(5678); + EXPECT_EQ(5678, engine()->GetToken()); + + engine()->CloseConnection(); + DestroyCommandBuffer(); + + EXPECT_EQ(BufferSyncInterface::NOT_CONNECTED, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + EXPECT_EQ(-1, engine()->Get()); + EXPECT_TRUE(engine()->parser() == NULL); +} + +// Checks that shared memory registration works. +TEST_F(CommandBufferEngineTest, TestSharedMemoryRegistration) { + // Create and register a first shared memory buffer. + const size_t kShmSize1 = 10; + RPCShmHandle shm1 = CreateShm(kShmSize1); + ASSERT_NE(kRPCInvalidHandle, shm1); + unsigned int shm_id1 = engine()->RegisterSharedMemory(shm1, kShmSize1); + EXPECT_NE(BufferSyncInterface::kInvalidSharedMemoryId, shm_id1); + EXPECT_TRUE(engine()->GetSharedMemoryAddress(shm_id1) != NULL); + EXPECT_EQ(kShmSize1, engine()->GetSharedMemorySize(shm_id1)); + + // Create and register a second shared memory buffer, check that it has a + // different memory location than the first one. + const size_t kShmSize2 = 25; + RPCShmHandle shm2 = CreateShm(kShmSize2); + ASSERT_NE(kRPCInvalidHandle, shm2); + unsigned int shm_id2 = engine()->RegisterSharedMemory(shm2, kShmSize2); + EXPECT_NE(BufferSyncInterface::kInvalidSharedMemoryId, shm_id2); + EXPECT_TRUE(engine()->GetSharedMemoryAddress(shm_id2) != NULL); + EXPECT_EQ(kShmSize2, engine()->GetSharedMemorySize(shm_id2)); + EXPECT_NE(shm_id1, shm_id2); + EXPECT_NE(engine()->GetSharedMemoryAddress(shm_id1), + engine()->GetSharedMemoryAddress(shm_id2)); + + // Create and register a third shared memory buffer, check that it has a + // different memory location than the first and second ones. + const size_t kShmSize3 = 33; + RPCShmHandle shm3 = CreateShm(kShmSize3); + ASSERT_NE(kRPCInvalidHandle, shm3); + unsigned int shm_id3 = engine()->RegisterSharedMemory(shm3, kShmSize3); + EXPECT_NE(BufferSyncInterface::kInvalidSharedMemoryId, shm_id3); + EXPECT_TRUE(engine()->GetSharedMemoryAddress(shm_id3) != NULL); + EXPECT_EQ(kShmSize3, engine()->GetSharedMemorySize(shm_id3)); + EXPECT_NE(shm_id1, shm_id3); + EXPECT_NE(shm_id2, shm_id3); + EXPECT_NE(engine()->GetSharedMemoryAddress(shm_id1), + engine()->GetSharedMemoryAddress(shm_id3)); + EXPECT_NE(engine()->GetSharedMemoryAddress(shm_id2), + engine()->GetSharedMemoryAddress(shm_id3)); + + engine()->UnregisterSharedMemory(shm_id1); + EXPECT_EQ(NULL, engine()->GetSharedMemoryAddress(shm_id1)); + EXPECT_EQ(0UL, engine()->GetSharedMemorySize(shm_id1)); + DestroyShm(shm1); + + engine()->UnregisterSharedMemory(shm_id2); + EXPECT_EQ(NULL, engine()->GetSharedMemoryAddress(shm_id2)); + EXPECT_EQ(0UL, engine()->GetSharedMemorySize(shm_id2)); + DestroyShm(shm2); + + engine()->UnregisterSharedMemory(shm_id3); + EXPECT_EQ(NULL, engine()->GetSharedMemoryAddress(shm_id2)); + EXPECT_EQ(0UL, engine()->GetSharedMemorySize(shm_id2)); + DestroyShm(shm3); +} + +// Checks that commands in the buffer are properly executed, and that the +// status/error stay valid. +TEST_F(CommandBufferEngineTest, TestCommandProcessing) { + engine()->InitConnection(); + CommandBufferEntry *entries = InitCommandBuffer(10, 0); + ASSERT_TRUE(entries != NULL); + + CommandBufferOffset get = engine()->Get(); + CommandBufferOffset put = get; + + // Create a command buffer with 3 commands + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 0, + 0, + NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 1, + 2, + args1); + + CommandBufferEntry args2[2]; + args2[0].value_uint32 = 5; + args2[1].value_float = 6.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 2, + 2, + args2); + + engine()->Put(put); + while (get != put) { + // Check that the parsing progresses, and that no error occurs. + CommandBufferOffset new_get = engine()->WaitGetChanges(get); + EXPECT_NE(get, new_get); + ASSERT_EQ(BufferSyncInterface::PARSING, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + EXPECT_EQ(new_get, engine()->Get()); + get = new_get; + } + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock()); + + engine()->CloseConnection(); + DestroyCommandBuffer(); +} + +// Checks that commands in the buffer are properly executed when wrapping the +// buffer, and that the status/error stay valid. +TEST_F(CommandBufferEngineTest, TestCommandWrapping) { + engine()->InitConnection(); + CommandBufferEntry *entries = InitCommandBuffer(10, 6); + ASSERT_TRUE(entries != NULL); + + CommandBufferOffset get = engine()->Get(); + CommandBufferOffset put = get; + + // Create a command buffer with 3 commands + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 0, + 0, + NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 1, + 2, + args1); + DCHECK_EQ(10, put); + put = 0; + + CommandBufferEntry args2[2]; + args2[0].value_uint32 = 5; + args2[1].value_float = 6.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 2, + 2, + args2); + + engine()->Put(put); + while (get != put) { + // Check that the parsing progresses, and that no error occurs. + CommandBufferOffset new_get = engine()->WaitGetChanges(get); + EXPECT_NE(get, new_get); + ASSERT_EQ(BufferSyncInterface::PARSING, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + EXPECT_EQ(new_get, engine()->Get()); + get = new_get; + } + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock()); + + engine()->CloseConnection(); + DestroyCommandBuffer(); +} + +// Checks that commands in the buffer are properly executed when we change the +// buffer, and when we close the connection. +TEST_F(CommandBufferEngineTest, TestSetBufferAndClose) { + engine()->InitConnection(); + CommandBufferEntry *entries = InitCommandBuffer(10, 0); + ASSERT_TRUE(entries != NULL); + + CommandBufferOffset get = engine()->Get(); + CommandBufferOffset put = get; + + // Create a command buffer with 3 commands + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 0, + 0, + NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 1, + 2, + args1); + + CommandBufferEntry args2[2]; + args2[0].value_uint32 = 5; + args2[1].value_float = 6.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 2, + 2, + args2); + + engine()->Put(put); + + // Setup a new buffer. + const size_t kShmSize = 10 * sizeof(CommandBufferEntry); // NOLINT + RPCShmHandle shm = CreateShm(kShmSize); + ASSERT_NE(kRPCInvalidHandle, shm); + unsigned int shm_id = engine()->RegisterSharedMemory(shm, kShmSize); + ASSERT_NE(BufferSyncInterface::kInvalidSharedMemoryId, shm_id); + CommandBufferEntry *entries2 = static_cast<CommandBufferEntry *>( + engine()->GetSharedMemoryAddress(shm_id)); + engine()->SetCommandBuffer(shm_id, 0, kShmSize, 0); + EXPECT_EQ(BufferSyncInterface::PARSING, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + EXPECT_EQ(0, engine()->Get()); + + // Destroy the old command buffer. + DestroyCommandBuffer(); + + get = engine()->Get(); + put = get; + put += AddCommandWithExpect(entries2 + put, + BufferSyncInterface::PARSE_NO_ERROR, + 1, + 2, + args1); + + engine()->Put(put); + + engine()->CloseConnection(); + // Check that all the commands did happen. + Mock::VerifyAndClearExpectations(api_mock()); + + engine()->UnregisterSharedMemory(shm_id); + DestroyShm(shm); +} + +// 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(CommandBufferEngineTest, TestRecoverableError) { + engine()->InitConnection(); + CommandBufferEntry *entries = InitCommandBuffer(10, 0); + ASSERT_TRUE(entries != NULL); + + CommandBufferOffset get = engine()->Get(); + CommandBufferOffset put = get; + + // Create a command buffer with 3 commands, 2 of them generating errors + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 0, + 0, + NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_INVALID_ARGUMENTS, + 1, + 2, + args1); + + CommandBufferEntry args2[2]; + args2[0].value_uint32 = 5; + args2[1].value_float = 6.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_UNKNOWN_COMMAND, + 2, + 2, + args2); + + engine()->Put(put); + while (get != put) { + // Check that the parsing progresses, and that no error occurs. + CommandBufferOffset new_get = engine()->WaitGetChanges(get); + EXPECT_NE(get, new_get); + ASSERT_EQ(BufferSyncInterface::PARSING, engine()->GetStatus()); + EXPECT_EQ(new_get, engine()->Get()); + get = new_get; + } + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock()); + + // Check that the error status was set to the first error. + EXPECT_EQ(BufferSyncInterface::PARSE_INVALID_ARGUMENTS, + engine()->GetParseError()); + // Check that the error status was reset after the query. + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + + engine()->CloseConnection(); + DestroyCommandBuffer(); +} + +// Checks that commands in the buffer are properly executed up to the point +// where a parsing error happened. Check that at that point the status and +// error are properly set. +TEST_F(CommandBufferEngineTest, TestNonRecoverableError) { + engine()->InitConnection(); + // Create a buffer with 6 entries, starting at entry 1, but allocate enough + // memory so that we can add commands that cross over the limit. + const size_t kShmSize = 10 * sizeof(CommandBufferEntry); // NOLINT + RPCShmHandle shm = CreateShm(kShmSize); + ASSERT_NE(kRPCInvalidHandle, shm); + unsigned int shm_id = engine()->RegisterSharedMemory(shm, kShmSize); + ASSERT_NE(BufferSyncInterface::kInvalidSharedMemoryId, shm_id); + CommandBufferEntry *entries = static_cast<CommandBufferEntry *>( + engine()->GetSharedMemoryAddress(shm_id)); + ASSERT_TRUE(entries != NULL); + engine()->SetCommandBuffer(shm_id, 0, 6 * sizeof(CommandBufferEntry), 1); + + CommandBufferOffset get = engine()->Get(); + CommandBufferOffset put = get; + + // Create a command buffer with 3 commands, the last one overlapping the end + // of the buffer. + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 0, + 0, + NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 1, + 2, + args1); + + CommandBufferOffset fail_get = put; + CommandHeader header; + header.size = 3; + header.command = 4; + entries[put++].value_header = header; + entries[put++].value_uint32 = 5; + entries[put++].value_uint32 = 6; + + // we should be beyond the end of the buffer now. + DCHECK_LT(6, put); + put = 0; + + engine()->Put(put); + while (get != put) { + // When the parsing stop progressing, break. + CommandBufferOffset new_get = engine()->WaitGetChanges(get); + if (new_get == get) { + EXPECT_EQ(new_get, engine()->Get()); + break; + } + get = new_get; + } + // We should be in an error case now. + EXPECT_EQ(BufferSyncInterface::PARSE_ERROR, engine()->GetStatus()); + // Check that the error status was set to the first error. + EXPECT_EQ(BufferSyncInterface::PARSE_OUT_OF_BOUNDS, + engine()->GetParseError()); + // Check that the error status was reset after the query. + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + + // Check that the valid commands did happen. + Mock::VerifyAndClearExpectations(api_mock()); + + engine()->CloseConnection(); + engine()->UnregisterSharedMemory(shm_id); + DestroyShm(shm); +} + +// Checks that HasWork() and DoWork() have the correct semantics. If there is +// work to do, DoWork should never block. +TEST_F(CommandBufferEngineTest, TestDoWork) { + engine()->InitConnection(); + CommandBufferEntry *entries = InitCommandBuffer(10, 0); + ASSERT_TRUE(entries != NULL); + + CommandBufferOffset get = engine()->Get(); + CommandBufferOffset put = get; + + // Test that if we have no message and no command we will block. + process_mock()->Reset(); + EXPECT_CALL(*process_mock(), HasMessage()).Times(AnyNumber()); + EXPECT_FALSE(engine()->HasWork()); + EXPECT_CALL(*process_mock(), ProcessMessage()); + EXPECT_TRUE(engine()->DoWork()); + + EXPECT_TRUE(process_mock()->would_have_blocked()); + Mock::VerifyAndClearExpectations(process_mock()); + + // Tests that messages get processed without blocking. + process_mock()->Reset(); + EXPECT_CALL(*process_mock(), HasMessage()).Times(AnyNumber()); + process_mock()->set_message_count(3); + EXPECT_TRUE(engine()->HasWork()); + + EXPECT_CALL(*process_mock(), ProcessMessage()).Times(3); + while (engine()->HasWork()) { + EXPECT_TRUE(engine()->DoWork()); + } + EXPECT_EQ(0, process_mock()->message_count()); + EXPECT_FALSE(process_mock()->would_have_blocked()); + Mock::VerifyAndClearExpectations(process_mock()); + + // Test that if we have commands, we will process them without blocking. + // Create a command buffer with 3 commands + process_mock()->Reset(); + EXPECT_CALL(*process_mock(), HasMessage()).Times(AnyNumber()); + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 0, + 0, + NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 1, + 2, + args1); + + CommandBufferEntry args2[2]; + args2[0].value_uint32 = 5; + args2[1].value_float = 6.f; + put += AddCommandWithExpect(entries + put, + BufferSyncInterface::PARSE_NO_ERROR, + 2, + 2, + args2); + + EXPECT_FALSE(engine()->HasWork()); // No work yet, until we change put + + engine()->Put(put); + EXPECT_TRUE(engine()->HasWork()); + + EXPECT_CALL(*process_mock(), ProcessMessage()).Times(0); + while (engine()->HasWork()) { + EXPECT_TRUE(engine()->DoWork()); + } + EXPECT_FALSE(process_mock()->would_have_blocked()); + get = engine()->Get(); + EXPECT_EQ(put, get); // once we're done, we should have executed everything. + ASSERT_EQ(BufferSyncInterface::PARSING, engine()->GetStatus()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, engine()->GetParseError()); + Mock::VerifyAndClearExpectations(process_mock()); + Mock::VerifyAndClearExpectations(api_mock()); + + // Test that the engine stops if we send it a "kill" message. + process_mock()->Reset(); + EXPECT_CALL(*process_mock(), HasMessage()).Times(AnyNumber()); + process_mock()->set_message_count(1); + EXPECT_TRUE(engine()->HasWork()); + + EXPECT_CALL(*process_mock(), ProcessMessage()).WillOnce(Return(false)); + EXPECT_FALSE(engine()->DoWork()); + Mock::VerifyAndClearExpectations(process_mock()); + + engine()->CloseConnection(); + DestroyCommandBuffer(); +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/cmd_parser.cc b/o3d/command_buffer/service/cross/cmd_parser.cc new file mode 100644 index 0000000..a640d3d --- /dev/null +++ b/o3d/command_buffer/service/cross/cmd_parser.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 file contains the implementation of the command parser. + +#include "command_buffer/service/cross/cmd_parser.h" + +namespace o3d { +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(0, 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. +BufferSyncInterface::ParseError CommandParser::ProcessCommand() { + CommandBufferOffset get = get_; + if (get == put_) return BufferSyncInterface::PARSE_NO_ERROR; + + CommandHeader header = buffer_[get].value_header; + if (header.size == 0) return BufferSyncInterface::PARSE_INVALID_SIZE; + if (header.size + get > entry_count_) + return BufferSyncInterface::PARSE_OUT_OF_BOUNDS; + BufferSyncInterface::ParseError result = handler_->DoCommand( + header.command, header.size - 1, buffer_ + get + 1); + get_ = (get + header.size) % entry_count_; + return result; +} + +// Processes all the commands, while the buffer is not empty. Stop if an error +// is encountered. +BufferSyncInterface::ParseError CommandParser::ProcessAllCommands() { + while (!IsEmpty()) { + BufferSyncInterface::ParseError error = ProcessCommand(); + if (error) return error; + } + return BufferSyncInterface::PARSE_NO_ERROR; +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/cmd_parser.h b/o3d/command_buffer/service/cross/cmd_parser.h new file mode 100644 index 0000000..84107c5 --- /dev/null +++ b/o3d/command_buffer/service/cross/cmd_parser.h @@ -0,0 +1,113 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_CMD_PARSER_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_CMD_PARSER_H__ + +#include "core/cross/types.h" +#include "command_buffer/common/cross/rpc.h" +#include "command_buffer/common/cross/buffer_sync_api.h" +#include "command_buffer/common/cross/cmd_buffer_format.h" + +namespace o3d { +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. + BufferSyncInterface::ParseError ProcessCommand(); + + // Processes all commands until get == put. + BufferSyncInterface::ParseError ProcessAllCommands(); + + 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. + // args: the arguments. + // Returns: + // BufferSyncInterface::NO_ERROR if no error was found, one of + // BufferSyncInterface::ParseError otherwise. + virtual BufferSyncInterface::ParseError DoCommand( + unsigned int command, + unsigned int arg_count, + CommandBufferEntry *args) = 0; +}; + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_CMD_PARSER_H__ diff --git a/o3d/command_buffer/service/cross/cmd_parser_test.cc b/o3d/command_buffer/service/cross/cmd_parser_test.cc new file mode 100644 index 0000000..a63489c --- /dev/null +++ b/o3d/command_buffer/service/cross/cmd_parser_test.cc @@ -0,0 +1,315 @@ +/* + * 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 "tests/common/win/testing_common.h" +#include "command_buffer/service/cross/cmd_parser.h" +#include "command_buffer/service/cross/mocks.h" + +namespace o3d { +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(BufferSyncInterface::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(0, parser->get()); + EXPECT_EQ(0, 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(BufferSyncInterface::PARSE_NO_ERROR, 123, 0, NULL); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, 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(BufferSyncInterface::PARSE_NO_ERROR, 456, 2, param_array); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, 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(BufferSyncInterface::PARSE_NO_ERROR, 789, 1, param_array); + param_array[1].value_int32 = 3434; + AddDoCommandExpect(BufferSyncInterface::PARSE_NO_ERROR, 2121, 1, + param_array+1); + + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, parser->ProcessCommand()); + EXPECT_EQ(put_cmd2, parser->get()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, 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(BufferSyncInterface::PARSE_NO_ERROR, 4545, 1, param_array); + param_array[1].value_int32 = 7878; + AddDoCommandExpect(BufferSyncInterface::PARSE_NO_ERROR, 6767, 1, + param_array+1); + + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, 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(BufferSyncInterface::PARSE_NO_ERROR, i, 0, NULL); + } + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, 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(BufferSyncInterface::PARSE_NO_ERROR, 3, 1, ¶m); + + DCHECK_EQ(5, put); + put = 0; + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, 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(BufferSyncInterface::PARSE_NO_ERROR, 4, 1, ¶m); + parser->set_put(put); + EXPECT_EQ(put, parser->put()); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, 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(BufferSyncInterface::PARSE_INVALID_SIZE, + 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(BufferSyncInterface::PARSE_OUT_OF_BOUNDS, + 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(BufferSyncInterface::PARSE_UNKNOWN_COMMAND, 3, 0, NULL); + EXPECT_EQ(BufferSyncInterface::PARSE_UNKNOWN_COMMAND, + 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(BufferSyncInterface::PARSE_NO_ERROR, 4, 0, NULL); + EXPECT_EQ(BufferSyncInterface::PARSE_NO_ERROR, parser->ProcessAllCommands()); + EXPECT_EQ(put, parser->get()); + Mock::VerifyAndClearExpectations(api_mock()); +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/effect_utils.cc b/o3d/command_buffer/service/cross/effect_utils.cc new file mode 100644 index 0000000..91beb17 --- /dev/null +++ b/o3d/command_buffer/service/cross/effect_utils.cc @@ -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 implements effect related utilities. + +#include "command_buffer/service/cross/effect_utils.h" + +namespace o3d { +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 +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/effect_utils.h b/o3d/command_buffer/service/cross/effect_utils.h new file mode 100644 index 0000000..2402254 --- /dev/null +++ b/o3d/command_buffer/service/cross/effect_utils.h @@ -0,0 +1,56 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_EFFECT_UTILS_H_ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_EFFECT_UTILS_H_ + +#include "command_buffer/common/cross/types.h" + +namespace o3d { +namespace command_buffer { + +// This function parses the data passed to the CREATE_EFFECT 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 +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_EFFECT_UTILS_H_ diff --git a/o3d/command_buffer/service/cross/effect_utils_test.cc b/o3d/command_buffer/service/cross/effect_utils_test.cc new file mode 100644 index 0000000..d37db9d --- /dev/null +++ b/o3d/command_buffer/service/cross/effect_utils_test.cc @@ -0,0 +1,86 @@ +/* + * 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 "tests/common/win/testing_common.h" +#include "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/command_buffer/service/cross/gapi_decoder.cc b/o3d/command_buffer/service/cross/gapi_decoder.cc new file mode 100644 index 0000000..efd3bc5 --- /dev/null +++ b/o3d/command_buffer/service/cross/gapi_decoder.cc @@ -0,0 +1,881 @@ +/* + * 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 "base/cross/bits.h" +#include "command_buffer/common/cross/gapi_interface.h" +#include "command_buffer/service/cross/gapi_decoder.h" +#include "command_buffer/service/cross/cmd_buffer_engine.h" + +namespace o3d { +namespace command_buffer { + +// Decode command with its arguments, and call the corresponding GAPIInterface +// 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. +BufferSyncInterface::ParseError GAPIDecoder::DoCommand( + unsigned int command, + unsigned int arg_count, + CommandBufferEntry *args) { + switch (command) { + case NOOP: + return BufferSyncInterface::PARSE_NO_ERROR; + case SET_TOKEN: + if (arg_count == 1) { + engine_->set_token(args[0].value_uint32); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case BEGIN_FRAME: + if (arg_count == 0) { + gapi_->BeginFrame(); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case END_FRAME: + if (arg_count == 0) { + gapi_->EndFrame(); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CLEAR: + if (arg_count == 7) { + unsigned int buffers = args[0].value_uint32; + if (buffers & ~GAPIInterface::ALL_BUFFERS) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + RGBA rgba; + rgba.red = args[1].value_float; + rgba.green = args[2].value_float; + rgba.blue = args[3].value_float; + rgba.alpha = args[4].value_float; + float depth = args[5].value_float; + unsigned int stencil = args[6].value_uint32; + gapi_->Clear(buffers, rgba, depth, stencil); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_VIEWPORT: + if (arg_count == 6) { + gapi_->SetViewport(args[0].value_uint32, + args[1].value_uint32, + args[2].value_uint32, + args[3].value_uint32, + args[4].value_float, + args[5].value_float); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_VERTEX_BUFFER: + if (arg_count == 3) { + return gapi_->CreateVertexBuffer(args[0].value_uint32, + args[1].value_uint32, + args[2].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DESTROY_VERTEX_BUFFER: + if (arg_count == 1) { + return gapi_->DestroyVertexBuffer(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_VERTEX_BUFFER_DATA_IMMEDIATE: { + if (arg_count < 2) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + ResourceID id = args[0].value_uint32; + unsigned int offset = args[1].value_uint32; + unsigned int size = (arg_count - 2) * sizeof(args[0]); + return gapi_->SetVertexBufferData(id, offset, size, args + 2); + } + case SET_VERTEX_BUFFER_DATA: + if (arg_count == 5) { + ResourceID id = args[0].value_uint32; + unsigned int offset = args[1].value_uint32; + unsigned int size = args[2].value_uint32; + void *data = GetAddressAndCheckSize(args[3].value_uint32, + args[4].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->SetVertexBufferData(id, offset, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case GET_VERTEX_BUFFER_DATA: + if (arg_count == 5) { + ResourceID id = args[0].value_uint32; + unsigned int offset = args[1].value_uint32; + unsigned int size = args[2].value_uint32; + void *data = GetAddressAndCheckSize(args[3].value_uint32, + args[4].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->GetVertexBufferData(id, offset, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_INDEX_BUFFER: + if (arg_count == 3) { + return gapi_->CreateIndexBuffer(args[0].value_uint32, + args[1].value_uint32, + args[2].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DESTROY_INDEX_BUFFER: + if (arg_count == 1) { + return gapi_->DestroyIndexBuffer(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_INDEX_BUFFER_DATA_IMMEDIATE: { + if (arg_count < 2) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + ResourceID id = args[0].value_uint32; + unsigned int offset = args[1].value_uint32; + unsigned int size = (arg_count - 2) * sizeof(args[0]); + return gapi_->SetIndexBufferData(id, offset, size, args + 2); + } + case SET_INDEX_BUFFER_DATA: + if (arg_count == 5) { + ResourceID id = args[0].value_uint32; + unsigned int offset = args[1].value_uint32; + unsigned int size = args[2].value_uint32; + void *data = GetAddressAndCheckSize(args[3].value_uint32, + args[4].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->SetIndexBufferData(id, offset, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case GET_INDEX_BUFFER_DATA: + if (arg_count == 5) { + ResourceID id = args[0].value_uint32; + unsigned int offset = args[1].value_uint32; + unsigned int size = args[2].value_uint32; + void *data = GetAddressAndCheckSize(args[3].value_uint32, + args[4].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->GetIndexBufferData(id, offset, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_VERTEX_STRUCT: + if (arg_count == 2) { + return gapi_->CreateVertexStruct(args[0].value_uint32, + args[1].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DESTROY_VERTEX_STRUCT: + if (arg_count == 1) { + return gapi_->DestroyVertexStruct(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_VERTEX_INPUT: + return DecodeSetVertexInput(arg_count, args); + case SET_VERTEX_STRUCT: + if (arg_count == 1) { + return gapi_->SetVertexStruct(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DRAW: + if (arg_count == 3) { + unsigned int primitive_type = args[0].value_uint32; + if (primitive_type >= GAPIInterface::MAX_PRIMITIVE_TYPE) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + unsigned int first = args[1].value_uint32; + unsigned int count = args[2].value_uint32; + return gapi_->Draw( + static_cast<GAPIInterface::PrimitiveType>(primitive_type), + first, count); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DRAW_INDEXED: + if (arg_count == 6) { + unsigned int primitive_type = args[0].value_uint32; + if (primitive_type >= GAPIInterface::MAX_PRIMITIVE_TYPE) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + ResourceID index_buffer = args[1].value_uint32; + unsigned int first = args[2].value_uint32; + unsigned int count = args[3].value_uint32; + unsigned int min_index = args[4].value_uint32; + unsigned int max_index = args[5].value_uint32; + return gapi_->DrawIndexed( + static_cast<GAPIInterface::PrimitiveType>(primitive_type), + index_buffer, first, count, min_index, max_index); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_EFFECT: + if (arg_count == 4) { + ResourceID id = args[0].value_uint32; + unsigned int size = args[1].value_uint32; + void *data = GetAddressAndCheckSize(args[2].value_uint32, + args[3].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->CreateEffect(id, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_EFFECT_IMMEDIATE: + if (arg_count > 2) { + ResourceID id = args[0].value_uint32; + unsigned int size = args[1].value_uint32; + if (size > (arg_count-2) * sizeof(args[0])) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->CreateEffect(id, size, args + 2); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DESTROY_EFFECT: + if (arg_count == 1) { + return gapi_->DestroyEffect(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_EFFECT: + if (arg_count == 1) { + return gapi_->SetEffect(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case GET_PARAM_COUNT: + if (arg_count == 4) { + ResourceID id = args[0].value_uint32; + unsigned int size = args[1].value_uint32; + void *data = GetAddressAndCheckSize(args[2].value_uint32, + args[3].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->GetParamCount(id, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_PARAM: + if (arg_count == 3) { + ResourceID param_id = args[0].value_uint32; + ResourceID effect_id = args[1].value_uint32; + unsigned int index = args[2].value_uint32; + return gapi_->CreateParam(param_id, effect_id, index); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_PARAM_BY_NAME: + if (arg_count == 5) { + ResourceID param_id = args[0].value_uint32; + ResourceID effect_id = args[1].value_uint32; + unsigned int size = args[2].value_uint32; + void *data = GetAddressAndCheckSize(args[3].value_uint32, + args[4].value_uint32, + size); + if (!data) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->CreateParamByName(param_id, effect_id, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_PARAM_BY_NAME_IMMEDIATE: + if (arg_count > 3) { + ResourceID param_id = args[0].value_uint32; + ResourceID effect_id = args[1].value_uint32; + unsigned int size = args[2].value_uint32; + if (size > (arg_count-1) * sizeof(args[0])) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->CreateParamByName(param_id, effect_id, size, args + 3); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DESTROY_PARAM: + if (arg_count == 1) { + return gapi_->DestroyParam(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_PARAM_DATA: + if (arg_count == 4) { + ResourceID id = args[0].value_uint32; + unsigned int size = args[1].value_uint32; + void *data = GetAddressAndCheckSize(args[2].value_uint32, + args[3].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->SetParamData(id, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_PARAM_DATA_IMMEDIATE: + if (arg_count > 2) { + ResourceID id = args[0].value_uint32; + unsigned int size = args[1].value_uint32; + if (size > (arg_count-2) * sizeof(args[0])) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->SetParamData(id, size, args + 2); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case GET_PARAM_DESC: + if (arg_count == 4) { + ResourceID id = args[0].value_uint32; + unsigned int size = args[1].value_uint32; + void *data = GetAddressAndCheckSize(args[2].value_uint32, + args[3].value_uint32, + size); + if (!data) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->GetParamDesc(id, size, data); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DESTROY_TEXTURE: + if (arg_count == 1) { + return gapi_->DestroyTexture(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case CREATE_TEXTURE_2D: + return DecodeCreateTexture2D(arg_count, args); + case CREATE_TEXTURE_3D: + return DecodeCreateTexture3D(arg_count, args); + case CREATE_TEXTURE_CUBE: + return DecodeCreateTextureCube(arg_count, args); + case SET_TEXTURE_DATA: + return DecodeSetTextureData(arg_count, args); + case SET_TEXTURE_DATA_IMMEDIATE: + return DecodeSetTextureDataImmediate(arg_count, args); + case GET_TEXTURE_DATA: + return DecodeGetTextureData(arg_count, args); + case CREATE_SAMPLER: + if (arg_count == 1) { + return gapi_->CreateSampler(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case DESTROY_SAMPLER: + if (arg_count == 1) { + return gapi_->DestroySampler(args[0].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_SAMPLER_STATES: + return DecodeSetSamplerStates(arg_count, args); + case SET_SAMPLER_BORDER_COLOR: + if (arg_count == 5) { + RGBA rgba; + rgba.red = args[1].value_float; + rgba.green = args[2].value_float; + rgba.blue = args[3].value_float; + rgba.alpha = args[4].value_float; + return gapi_->SetSamplerBorderColor(args[0].value_uint32, rgba); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_SAMPLER_TEXTURE: + if (arg_count == 2) { + return gapi_->SetSamplerTexture(args[0].value_uint32, + args[1].value_uint32); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_SCISSOR: + if (arg_count == 2) { + namespace cmd = set_scissor; + Uint32 x_y_enable = args[0].value_uint32; + if (cmd::Unused::Get(x_y_enable) != 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + unsigned int x = cmd::X::Get(x_y_enable); + unsigned int y = cmd::Y::Get(x_y_enable); + bool enable = cmd::Enable::Get(x_y_enable) != 0; + Uint32 width_height = args[1].value_uint32; + unsigned int width = cmd::Width::Get(width_height); + unsigned int height = cmd::Height::Get(width_height); + gapi_->SetScissor(enable, x, y, width, height); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_POLYGON_OFFSET: + if (arg_count == 2) { + gapi_->SetPolygonOffset(args[0].value_float, args[1].value_float); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_POINT_LINE_RASTER: + if (arg_count == 2) { + namespace cmd = set_point_line_raster; + Uint32 enables = args[0].value_uint32; + if (cmd::Unused::Get(enables) != 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + bool line_smooth = cmd::LineSmoothEnable::Get(enables); + bool point_sprite = cmd::PointSpriteEnable::Get(enables); + float point_size = args[1].value_float; + gapi_->SetPointLineRaster(line_smooth, point_sprite, point_size); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_POLYGON_RASTER: + if (arg_count == 1) { + namespace cmd = set_polygon_raster; + Uint32 fill_cull = args[0].value_uint32; + unsigned int fill_value = cmd::FillMode::Get(fill_cull); + unsigned int cull_value = cmd::CullMode::Get(fill_cull); + if (cmd::Unused::Get(fill_cull) != 0 || + fill_value >= GAPIInterface::NUM_POLYGON_MODE || + cull_value >= GAPIInterface::NUM_FACE_CULL_MODE) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + gapi_->SetPolygonRaster( + static_cast<GAPIInterface::PolygonMode>(fill_value), + static_cast<GAPIInterface::FaceCullMode>(cull_value)); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_ALPHA_TEST: + if (arg_count == 2) { + namespace cmd = set_alpha_test; + Uint32 func_enable = args[0].value_uint32; + if (cmd::Unused::Get(func_enable) != 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // Check that the bitmask get cannot generate values outside of the + // allowed range. + COMPILE_ASSERT(cmd::Func::kMask < GAPIInterface::NUM_COMPARISON, + set_alpha_test_Func_may_produce_invalid_values); + GAPIInterface::Comparison comp = + static_cast<GAPIInterface::Comparison>(cmd::Func::Get(func_enable)); + bool enable = cmd::Enable::Get(func_enable) != 0; + gapi_->SetAlphaTest(enable, args[1].value_float, comp); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_DEPTH_TEST: + if (arg_count == 1) { + namespace cmd = set_depth_test; + Uint32 func_enable = args[0].value_uint32; + if (cmd::Unused::Get(func_enable) != 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // Check that the bitmask get cannot generate values outside of the + // allowed range. + COMPILE_ASSERT(cmd::Func::kMask < GAPIInterface::NUM_COMPARISON, + set_alpha_test_Func_may_produce_invalid_values); + GAPIInterface::Comparison comp = + static_cast<GAPIInterface::Comparison>(cmd::Func::Get(func_enable)); + bool write_enable = cmd::WriteEnable::Get(func_enable) != 0; + bool enable = cmd::Enable::Get(func_enable) != 0; + gapi_->SetDepthTest(enable, write_enable, comp); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_STENCIL_TEST: + return DecodeSetStencilTest(arg_count, args); + case SET_COLOR_WRITE: + if (arg_count == 1) { + namespace cmd = set_color_write; + Uint32 enables = args[0].value_uint32; + if (cmd::Unused::Get(enables) != 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + bool red = cmd::RedMask::Get(enables) != 0; + bool green = cmd::GreenMask::Get(enables) != 0; + bool blue = cmd::BlueMask::Get(enables) != 0; + bool alpha = cmd::AlphaMask::Get(enables) != 0; + bool dither = cmd::DitherEnable::Get(enables) != 0; + gapi_->SetColorWrite(red, green, blue, alpha, dither); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + case SET_BLENDING: + return DecodeSetBlending(arg_count, args); + case SET_BLENDING_COLOR: + if (arg_count == 4) { + RGBA rgba; + rgba.red = args[0].value_float; + rgba.green = args[1].value_float; + rgba.blue = args[2].value_float; + rgba.alpha = args[3].value_float; + gapi_->SetBlendingColor(rgba); + return BufferSyncInterface::PARSE_NO_ERROR; + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + default: + return BufferSyncInterface::PARSE_UNKNOWN_COMMAND; + } +} + +// Decodes the SET_VERTEX_INPUT command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeSetVertexInput( + unsigned int arg_count, + CommandBufferEntry *args) { + namespace cmd = set_vertex_input_cmd; + if (arg_count != 5) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + ResourceID vertex_struct_id = args[0].value_uint32; + unsigned int input_index = args[1].value_uint32; + ResourceID vertex_buffer_id = args[2].value_uint32; + unsigned int offset = args[3].value_uint32; + unsigned int type_stride_semantic = args[4].value_uint32; + unsigned int semantic_index = cmd::SemanticIndex::Get(type_stride_semantic); + unsigned int semantic = cmd::Semantic::Get(type_stride_semantic); + unsigned int type = cmd::Type::Get(type_stride_semantic); + unsigned int stride = cmd::Stride::Get(type_stride_semantic); + if (semantic >= vertex_struct::NUM_SEMANTICS || + type >= vertex_struct::NUM_TYPES || stride == 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->SetVertexInput(vertex_struct_id, input_index, vertex_buffer_id, + offset, stride, + static_cast<vertex_struct::Type>(type), + static_cast<vertex_struct::Semantic>(semantic), + semantic_index); +} + +// Decodes the CREATE_TEXTURE_2D command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeCreateTexture2D( + unsigned int arg_count, + CommandBufferEntry *args) { + if (arg_count == 3) { + namespace cmd = create_texture_2d_cmd; + unsigned int id = args[0].value_uint32; + unsigned int width_height = args[1].value_uint32; + unsigned int levels_format_flags = args[2].value_uint32; + unsigned int width = cmd::Width::Get(width_height); + unsigned int height = cmd::Height::Get(width_height); + unsigned int levels = cmd::Levels::Get(levels_format_flags); + unsigned int unused = cmd::Unused::Get(levels_format_flags); + unsigned int format = cmd::Format::Get(levels_format_flags); + unsigned int flags = cmd::Flags::Get(levels_format_flags); + unsigned int max_levels = + 1 + base::bits::Log2Ceiling(std::max(width, height)); + if ((width == 0) || (height == 0) || (levels > max_levels) || + (unused != 0) || (format >= texture::NUM_FORMATS)) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + if (levels == 0) levels = max_levels; + return gapi_->CreateTexture2D(id, width, height, levels, + static_cast<texture::Format>(format), flags); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } +} + +// Decodes the CREATE_TEXTURE_3D command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeCreateTexture3D( + unsigned int arg_count, + CommandBufferEntry *args) { + if (arg_count == 4) { + namespace cmd = create_texture_3d_cmd; + unsigned int id = args[0].value_uint32; + unsigned int width_height = args[1].value_uint32; + unsigned int depth_unused = args[2].value_uint32; + unsigned int levels_format_flags = args[3].value_uint32; + unsigned int width = cmd::Width::Get(width_height); + unsigned int height = cmd::Height::Get(width_height); + unsigned int depth = cmd::Depth::Get(depth_unused); + unsigned int unused1 = cmd::Unused1::Get(depth_unused); + unsigned int levels = cmd::Levels::Get(levels_format_flags); + unsigned int unused2 = cmd::Unused2::Get(levels_format_flags); + unsigned int format = cmd::Format::Get(levels_format_flags); + unsigned int flags = cmd::Flags::Get(levels_format_flags); + unsigned int max_levels = + 1 + 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::NUM_FORMATS)) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + if (levels == 0) levels = max_levels; + return gapi_->CreateTexture3D(id, width, height, depth, levels, + static_cast<texture::Format>(format), flags); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } +} + +// Decodes the CREATE_TEXTURE_CUBE command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeCreateTextureCube( + unsigned int arg_count, + CommandBufferEntry *args) { + if (arg_count == 3) { + namespace cmd = create_texture_cube_cmd; + unsigned int id = args[0].value_uint32; + unsigned int side_unused = args[1].value_uint32; + unsigned int levels_format_flags = args[2].value_uint32; + unsigned int side = cmd::Side::Get(side_unused); + unsigned int unused1 = cmd::Unused1::Get(side_unused); + unsigned int levels = cmd::Levels::Get(levels_format_flags); + unsigned int unused2 = cmd::Unused2::Get(levels_format_flags); + unsigned int format = cmd::Format::Get(levels_format_flags); + unsigned int flags = cmd::Flags::Get(levels_format_flags); + unsigned int max_levels = 1 + base::bits::Log2Ceiling(side); + if ((side == 0) || (levels > max_levels) || (unused1 != 0) || + (unused2 != 0) || (format >= texture::NUM_FORMATS)) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + if (levels == 0) levels = max_levels; + return gapi_->CreateTextureCube(id, side, levels, + static_cast<texture::Format>(format), + flags); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } +} + +// Decodes the SET_TEXTURE_DATA command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeSetTextureData( + unsigned int arg_count, + CommandBufferEntry *args) { + if (arg_count == 10) { + namespace cmd = set_texture_data_cmd; + unsigned int id = args[0].value_uint32; + unsigned int x_y = args[1].value_uint32; + unsigned int width_height = args[2].value_uint32; + unsigned int z_depth = args[3].value_uint32; + unsigned int level_face = args[4].value_uint32; + unsigned int row_pitch = args[5].value_uint32; + unsigned int slice_pitch = args[6].value_uint32; + unsigned int size = args[7].value_uint32; + unsigned int shm_id = args[8].value_uint32; + unsigned int offset = args[9].value_uint32; + unsigned int x = cmd::X::Get(x_y); + unsigned int y = cmd::Y::Get(x_y); + unsigned int width = cmd::Width::Get(width_height); + unsigned int height = cmd::Height::Get(width_height); + unsigned int z = cmd::Z::Get(z_depth); + unsigned int depth = cmd::Depth::Get(z_depth); + unsigned int level = cmd::Level::Get(level_face); + unsigned int face = cmd::Face::Get(level_face); + unsigned int unused = cmd::Unused::Get(level_face); + const void *data = GetAddressAndCheckSize(shm_id, offset, size); + if (face >= 6 || unused != 0 || !data) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->SetTextureData(id, x, y, z, width, height, depth, level, + static_cast<texture::Face>(face), row_pitch, + slice_pitch, size, data); + + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } +} + +// Decodes the SET_TEXTURE_DATA_IMMEDIATE command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeSetTextureDataImmediate( + unsigned int arg_count, + CommandBufferEntry *args) { + if (arg_count > 8) { + namespace cmd = set_texture_data_immediate_cmd; + unsigned int id = args[0].value_uint32; + unsigned int x_y = args[1].value_uint32; + unsigned int width_height = args[2].value_uint32; + unsigned int z_depth = args[3].value_uint32; + unsigned int level_face = args[4].value_uint32; + unsigned int row_pitch = args[5].value_uint32; + unsigned int slice_pitch = args[6].value_uint32; + unsigned int size = args[7].value_uint32; + unsigned int x = cmd::X::Get(x_y); + unsigned int y = cmd::Y::Get(x_y); + unsigned int width = cmd::Width::Get(width_height); + unsigned int height = cmd::Height::Get(width_height); + unsigned int z = cmd::Z::Get(z_depth); + unsigned int depth = cmd::Depth::Get(z_depth); + unsigned int level = cmd::Level::Get(level_face); + unsigned int face = cmd::Face::Get(level_face); + unsigned int unused = cmd::Unused::Get(level_face); + if (face >= 6 || unused != 0 || + size > (arg_count - 5) * sizeof(args[0])) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->SetTextureData(id, x, y, z, width, height, depth, level, + static_cast<texture::Face>(face), row_pitch, + slice_pitch, size, args + 8); + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } +} + +// Decodes the GET_TEXTURE_DATA command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeGetTextureData( + unsigned int arg_count, + CommandBufferEntry *args) { + if (arg_count == 10) { + namespace cmd = get_texture_data_cmd; + unsigned int id = args[0].value_uint32; + unsigned int x_y = args[1].value_uint32; + unsigned int width_height = args[2].value_uint32; + unsigned int z_depth = args[3].value_uint32; + unsigned int level_face = args[4].value_uint32; + unsigned int row_pitch = args[5].value_uint32; + unsigned int slice_pitch = args[6].value_uint32; + unsigned int size = args[7].value_uint32; + unsigned int shm_id = args[8].value_uint32; + unsigned int offset = args[9].value_uint32; + unsigned int x = cmd::X::Get(x_y); + unsigned int y = cmd::Y::Get(x_y); + unsigned int width = cmd::Width::Get(width_height); + unsigned int height = cmd::Height::Get(width_height); + unsigned int z = cmd::Z::Get(z_depth); + unsigned int depth = cmd::Depth::Get(z_depth); + unsigned int level = cmd::Level::Get(level_face); + unsigned int face = cmd::Face::Get(level_face); + unsigned int unused = cmd::Unused::Get(level_face); + void *data = GetAddressAndCheckSize(shm_id, offset, size); + if (face >= 6 || unused != 0 || !data) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return gapi_->GetTextureData(id, x, y, z, width, height, depth, level, + static_cast<texture::Face>(face), row_pitch, + slice_pitch, size, data); + + } else { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } +} + +// Decodes the SET_SAMPLER_STATES command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeSetSamplerStates( + unsigned int arg_count, + CommandBufferEntry *args) { + namespace cmd = set_sampler_states; + if (arg_count != 2) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + ResourceID id = args[0].value_uint32; + Uint32 arg = args[1].value_uint32; + if (cmd::Unused::Get(arg) != 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + unsigned int address_u_value = cmd::AddressingU::Get(arg); + unsigned int address_v_value = cmd::AddressingV::Get(arg); + unsigned int address_w_value = cmd::AddressingW::Get(arg); + unsigned int mag_filter_value = cmd::MagFilter::Get(arg); + unsigned int min_filter_value = cmd::MinFilter::Get(arg); + unsigned int mip_filter_value = cmd::MipFilter::Get(arg); + unsigned int max_anisotropy = cmd::MaxAnisotropy::Get(arg); + if (address_u_value >= sampler::NUM_ADDRESSING_MODE || + address_v_value >= sampler::NUM_ADDRESSING_MODE || + address_w_value >= sampler::NUM_ADDRESSING_MODE || + mag_filter_value >= sampler::NUM_FILTERING_MODE || + min_filter_value >= sampler::NUM_FILTERING_MODE || + mip_filter_value >= sampler::NUM_FILTERING_MODE || + mag_filter_value == sampler::NONE || + min_filter_value == sampler::NONE || + max_anisotropy == 0) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + gapi_->SetSamplerStates( + 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 BufferSyncInterface::PARSE_NO_ERROR; +} + +// Decodes the SET_STENCIL_TEST command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeSetStencilTest( + unsigned int arg_count, + CommandBufferEntry *args) { + namespace cmd = set_stencil_test; + if (arg_count != 2) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + Uint32 arg0 = args[0].value_uint32; + Uint32 arg1 = args[1].value_uint32; + if (cmd::Unused0::Get(arg0) != 0 || + cmd::Unused1::Get(arg1) != 0 || + cmd::Unused2::Get(arg1) != 0) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + unsigned int write_mask = cmd::WriteMask::Get(arg0); + unsigned int compare_mask = cmd::CompareMask::Get(arg0); + unsigned int ref = cmd::ReferenceValue::Get(arg0); + bool enable = cmd::Enable::Get(arg0) != 0; + bool separate_ccw = cmd::SeparateCCW::Get(arg0) != 0; + gapi_->SetStencilTest(enable, separate_ccw, write_mask, compare_mask, ref, + arg1); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +// Decodes the SET_BLENDING command. +BufferSyncInterface::ParseError GAPIDecoder::DecodeSetBlending( + unsigned int arg_count, + CommandBufferEntry *args) { + namespace cmd = set_blending; + if (arg_count != 1) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + Uint32 arg = args[0].value_uint32; + bool enable = cmd::Enable::Get(arg) != 0; + bool separate_alpha = cmd::SeparateAlpha::Get(arg) != 0; + unsigned int color_eq = cmd::ColorEq::Get(arg); + unsigned int color_src = cmd::ColorSrcFunc::Get(arg); + unsigned int color_dst = cmd::ColorDstFunc::Get(arg); + unsigned int alpha_eq = cmd::AlphaEq::Get(arg); + unsigned int alpha_src = cmd::AlphaSrcFunc::Get(arg); + unsigned int alpha_dst = cmd::AlphaDstFunc::Get(arg); + if (cmd::Unused0::Get(arg) != 0 || + cmd::Unused1::Get(arg) != 0 || + color_eq >= GAPIInterface::NUM_BLEND_EQ || + color_src >= GAPIInterface::NUM_BLEND_FUNC || + color_dst >= GAPIInterface::NUM_BLEND_FUNC || + alpha_eq >= GAPIInterface::NUM_BLEND_EQ || + alpha_src >= GAPIInterface::NUM_BLEND_FUNC || + alpha_dst >= GAPIInterface::NUM_BLEND_FUNC) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + gapi_->SetBlending(enable, + separate_alpha, + static_cast<GAPIInterface::BlendEq>(color_eq), + static_cast<GAPIInterface::BlendFunc>(color_src), + static_cast<GAPIInterface::BlendFunc>(color_dst), + static_cast<GAPIInterface::BlendEq>(alpha_eq), + static_cast<GAPIInterface::BlendFunc>(alpha_src), + static_cast<GAPIInterface::BlendFunc>(alpha_dst)); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +void *GAPIDecoder::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); + if (offset + size > shm_size) return NULL; + return static_cast<char *>(shm_addr) + offset; +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/gapi_decoder.h b/o3d/command_buffer/service/cross/gapi_decoder.h new file mode 100644 index 0000000..615bef6 --- /dev/null +++ b/o3d/command_buffer/service/cross/gapi_decoder.h @@ -0,0 +1,131 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_GAPI_DECODER_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_GAPI_DECODER_H__ + +#include "core/cross/types.h" +#include "command_buffer/service/cross/cmd_parser.h" + +namespace o3d { +namespace command_buffer { + +class GAPIInterface; +class CommandBufferEngine; + +// This class implements the AsyncAPIInterface interface, decoding GAPI +// commands and sending them to a GAPI interface. +class GAPIDecoder : public AsyncAPIInterface { + public: + typedef BufferSyncInterface::ParseError ParseError; + + explicit GAPIDecoder(GAPIInterface *gapi) : gapi_(gapi), engine_(NULL) {} + virtual ~GAPIDecoder() {} + // Executes a command. + // Parameters: + // command: the command index. + // arg_count: the number of CommandBufferEntry arguments. + // args: the arguments. + // Returns: + // BufferSyncInterface::NO_ERROR if no error was found, one of + // BufferSyncInterface::ParseError otherwise. + virtual ParseError DoCommand(unsigned int command, + unsigned int arg_count, + CommandBufferEntry *args); + + // Sets the engine, to get shared memory buffers from, and to set the token + // to. + void set_engine(CommandBufferEngine *engine) { engine_ = engine; } + private: + // Decodes the SET_VERTEX_INPUT command. + ParseError DecodeSetVertexInput(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the CREATE_TEXTURE_2D command. + ParseError DecodeCreateTexture2D(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the CREATE_TEXTURE_3D command. + ParseError DecodeCreateTexture3D(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the CREATE_TEXTURE_CUBE command. + ParseError DecodeCreateTextureCube(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the SET_TEXTURE_DATA command. + ParseError DecodeSetTextureData(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the SET_TEXTURE_DATA_IMMEDIATE command. + ParseError DecodeSetTextureDataImmediate(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the GET_TEXTURE_DATA command. + ParseError DecodeGetTextureData(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the SET_SAMPLER_STATES command. + ParseError DecodeSetSamplerStates(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the SET_STENCIL_TEST command. + ParseError DecodeSetStencilTest(unsigned int arg_count, + CommandBufferEntry *args); + + // Decodes the SET_BLENDING command. + ParseError DecodeSetBlending(unsigned int arg_count, + CommandBufferEntry *args); + + // 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); + GAPIInterface *gapi_; + CommandBufferEngine *engine_; +}; + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_GAPI_DECODER_H__ diff --git a/o3d/command_buffer/service/cross/gl/effect_gl.cc b/o3d/command_buffer/service/cross/gl/effect_gl.cc new file mode 100644 index 0000000..9343d96 --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/effect_gl.cc @@ -0,0 +1,633 @@ +/* + * 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 <map> + +#include "base/cross/std_functional.h" +#include "command_buffer/service/cross/gl/gapi_gl.h" +#include "command_buffer/service/cross/effect_utils.h" + +namespace o3d { +namespace command_buffer { + +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::FLOAT1; + case CG_FLOAT2: + return effect_param::FLOAT2; + case CG_FLOAT3: + return effect_param::FLOAT3; + case CG_FLOAT4: + return effect_param::FLOAT4; + case CG_INT: + case CG_INT1: + return effect_param::INT; + case CG_BOOL: + case CG_BOOL1: + return effect_param::BOOL; + case CG_FLOAT4x4: + return effect_param::MATRIX4; + case CG_SAMPLER: + case CG_SAMPLER1D: + case CG_SAMPLER2D: + case CG_SAMPLER3D: + case CG_SAMPLERCUBE: + return effect_param::SAMPLER; + default : { + DLOG(INFO) << "Cannot convert CGtype " + << cgGetTypeString(cg_type) + << " to a Param type."; + return effect_param::UNKNOWN; + } + } +} + +EffectParamGL *EffectParamGL::Create(EffectGL *effect, + unsigned int index) { + DCHECK(effect); + const EffectGL::LowLevelParam &low_level_param = + effect->low_level_params_[index]; + CGtype cg_type = + cgGetParameterType(EffectGL::GetEitherCgParameter(low_level_param)); + effect_param::DataType type = CgTypeToCBType(cg_type); + if (type == effect_param::UNKNOWN) 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); + 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->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_]; + 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::FLOAT1: + 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::FLOAT2: + 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::FLOAT3: + 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::FLOAT4: + 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::MATRIX4: + 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::INT: + 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::BOOL: { + 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::SAMPLER: { + low_level_param.sampler_id = *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 (ParamResourceList::iterator it = resource_params_.begin(); + it != resource_params_.end(); ++it) { + (*it)->ResetEffect(); + } +} + +void EffectGL::LinkParam(EffectParamGL *param) { + resource_params_.push_back(param); +} + +void EffectGL::UnlinkParam(EffectParamGL *param) { + std::remove(resource_params_.begin(), resource_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(CGparameter cg_param, + bool vp) { + // Loop over all *leaf* parameters, visiting only CGparameters that have + // had storage allocated to them. + for (; cg_param != NULL; cg_param = cgGetNextLeafParameter(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; + + int index = GetLowLevelParamIndexByName(name); + if (index < 0) { + LowLevelParam param = {name, NULL, NULL, kInvalidResource}; + index = low_level_params_.size(); + low_level_params_.push_back(param); + CGtype cg_type = cgGetParameterType(cg_param); + 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 (vp) { + low_level_params_[index].vp_param = cg_param; + } else { + low_level_params_[index].fp_param = cg_param; + } + } +} + +void EffectGL::Initialize() { + AddLowLevelParams(cgGetFirstLeafParameter(vertex_program_, CG_PROGRAM), true); + AddLowLevelParams(cgGetFirstLeafParameter(vertex_program_, CG_GLOBAL), true); + AddLowLevelParams(cgGetFirstLeafParameter(fragment_program_, CG_PROGRAM), + false); + AddLowLevelParams(cgGetFirstLeafParameter(fragment_program_, CG_GLOBAL), + false); +} + +// 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]; + ResourceID id = low_level_params_[param_index].sampler_id; + 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]; + ResourceID id = ll_param.sampler_id; + 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 a handle to the selected parameter, and wraps it into an +// EffectParamGL if successful. +EffectParamGL *EffectGL::CreateParam(unsigned int index) { + if (index < low_level_params_.size()) return NULL; + return EffectParamGL::Create(this, index); +} + +// 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; + EffectParamGL::Create(this, index); +} + +BufferSyncInterface::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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + EffectGL * effect = EffectGL::Create(this, effect_code, + vertex_program_entry, + fragment_program_entry); + if (!effect) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + effects_.Assign(id, effect); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::DestroyEffect(ResourceID id) { + if (id == current_effect_id_) DirtyEffect(); + return effects_.Destroy(id) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::ParseError GAPIGL::SetEffect(ResourceID id) { + DirtyEffect(); + current_effect_id_ = id; + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::GetParamCount(ResourceID id, + unsigned int size, + void *data) { + EffectGL *effect = effects_.Get(id); + if (!effect || size < sizeof(Uint32)) // NOLINT + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + *static_cast<Uint32 *>(data) = effect->GetParamCount(); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::CreateParam(ResourceID param_id, + ResourceID effect_id, + unsigned int index) { + EffectGL *effect = effects_.Get(effect_id); + if (!effect) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + EffectParamGL *param = effect->CreateParam(index); + if (!param) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + effect_params_.Assign(param_id, param); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::CreateParamByName(ResourceID param_id, + ResourceID effect_id, + unsigned int size, + const void *name) { + EffectGL *effect = effects_.Get(effect_id); + if (!effect) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + std::string string_name(static_cast<const char *>(name), size); + EffectParamGL *param = effect->CreateParamByName(string_name.c_str()); + if (!param) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + effect_params_.Assign(param_id, param); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::DestroyParam(ResourceID id) { + return effect_params_.Destroy(id) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::ParseError GAPIGL::SetParamData(ResourceID id, + unsigned int size, + const void *data) { + EffectParamGL *param = effect_params_.Get(id); + if (!param) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return param->SetData(this, size, data) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::ParseError GAPIGL::GetParamDesc(ResourceID id, + unsigned int size, + void *data) { + EffectParamGL *param = effect_params_.Get(id); + if (!param) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return param->GetDesc(size, data) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +// 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 command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/gl/effect_gl.h b/o3d/command_buffer/service/cross/gl/effect_gl.h new file mode 100644 index 0000000..fc29bf3 --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/effect_gl.h @@ -0,0 +1,151 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_EFFECT_GL_H_ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_EFFECT_GL_H_ + +#include <vector> +#include "command_buffer/common/cross/gapi_interface.h" +#include "command_buffer/service/cross/resource.h" +#include "command_buffer/service/cross/gl/gl_utils.h" + +namespace o3d { +namespace command_buffer { + +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; + + // Creates an effect parameter with the specified index. + EffectParamGL *CreateParam(unsigned int index); + + // 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; + ResourceID sampler_id; + }; + typedef std::vector<LowLevelParam> LowLevelParamList; + typedef std::vector<EffectParamGL *> ParamResourceList; + + 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(CGparameter cg_param, bool vp); + + // 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 Param resources created. + ParamResourceList resource_params_; + // 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 command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_EFFECT_GL_H_ diff --git a/o3d/command_buffer/service/cross/gl/gapi_gl.cc b/o3d/command_buffer/service/cross/gl/gapi_gl.cc new file mode 100644 index 0000000..a09375a --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/gapi_gl.cc @@ -0,0 +1,141 @@ +/* + * 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 <build/build_config.h> +#include "command_buffer/service/cross/gl/gl_utils.h" +#include "command_buffer/service/cross/gl/gapi_gl.h" + +#ifdef OS_LINUX +#include "command_buffer/service/linux/x_utils.h" +#endif // OS_LINUX + +namespace o3d { +namespace command_buffer { + +GAPIGL::GAPIGL() +#ifdef OS_LINUX + : window_(NULL), +#endif + cg_context_(NULL), + 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() { +#ifdef OS_LINUX + DCHECK(window_); + if (!window_->Initialize()) + return false; + if (!window_->MakeCurrent()) + return false; + InitCommon(); + CHECK_GL_ERROR(); + return true; +#else + return false; +#endif +} + +void GAPIGL::InitCommon() { + 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); + // 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(); +} + +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_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 & COLOR ? GL_COLOR_BUFFER_BIT : 0) | + (buffers & DEPTH ? GL_DEPTH_BUFFER_BIT : 0) | + (buffers & STENCIL ? GL_STENCIL_BUFFER_BIT : 0)); + CHECK_GL_ERROR(); +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/gl/gapi_gl.h b/o3d/command_buffer/service/cross/gl/gapi_gl.h new file mode 100644 index 0000000..8d71800 --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/gapi_gl.h @@ -0,0 +1,395 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GAPI_GL_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GAPI_GL_H__ + +#include <build/build_config.h> +#include "command_buffer/common/cross/gapi_interface.h" +#include "command_buffer/service/cross/gl/gl_utils.h" +#include "command_buffer/service/cross/gl/effect_gl.h" +#include "command_buffer/service/cross/gl/geometry_gl.h" +#include "command_buffer/service/cross/gl/sampler_gl.h" +#include "command_buffer/service/cross/gl/texture_gl.h" + +namespace o3d { +namespace command_buffer { +#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; } +#endif + + // 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 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 CreateTexture2D function for GL. + virtual ParseError CreateTexture2D(ResourceID id, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags); + + // 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); + + // Implements the CreateTextureCube function for GL. + virtual ParseError CreateTextureCube(ResourceID id, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags); + + // 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); + + // 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); + } + + 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: + void 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(); + +#ifdef OS_LINUX + XWindowWrapper *window_; +#endif + CGcontext cg_context_; + + ResourceID current_vertex_struct_; + bool validate_streams_; + unsigned int max_vertices_; + ResourceID current_effect_id_; + bool validate_effect_; + EffectGL *current_effect_; + + 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_; +}; + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GAPI_GL_H__ diff --git a/o3d/command_buffer/service/cross/gl/geometry_gl.cc b/o3d/command_buffer/service/cross/gl/geometry_gl.cc new file mode 100644 index 0000000..2dd8758 --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/geometry_gl.cc @@ -0,0 +1,548 @@ +/* + * 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 "command_buffer/service/cross/gl/gapi_gl.h" +#include "command_buffer/service/cross/gl/geometry_gl.h" + +namespace o3d { +namespace command_buffer { + +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::DYNAMIC) ? 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::DYNAMIC) ? 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 const GLvoid *OffsetToPtr(GLintptr offset) { + return static_cast<char *>(NULL) + 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::POSITION: + DCHECK_EQ(semantic_index, 0); + return 0; + case vertex_struct::NORMAL: + DCHECK_EQ(semantic_index, 0); + return 2; + case vertex_struct::COLOR: + DCHECK_LT(semantic_index, 2); + return 3 + semantic_index; + case vertex_struct::TEX_COORD: + DCHECK_LT(semantic_index, 8); + return 8 + semantic_index; + default: + DLOG(FATAL) << "Not reached."; + break; + } +} + +inline void ExtractSizeTypeNormalized(vertex_struct::Type type, + GLint *size, + GLenum *gl_type, + GLboolean *normalized) { + switch (type) { + case vertex_struct::FLOAT1: + case vertex_struct::FLOAT2: + case vertex_struct::FLOAT3: + case vertex_struct::FLOAT4: + *size = type - vertex_struct::FLOAT1 + 1; + *gl_type = GL_FLOAT; + *normalized = false; + break; + case vertex_struct::UCHAR4N: + *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 = element.offset; + } + dirty_ = false; +} + +BufferSyncInterface::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 BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::DestroyVertexBuffer(ResourceID id) { + return vertex_buffers_.Destroy(id) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return vertex_buffer->SetData(offset, size, data) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::ParseError GAPIGL::GetVertexBufferData(ResourceID id, + unsigned int offset, + unsigned int size, + void *data) { + VertexBufferGL *vertex_buffer = vertex_buffers_.Get(id); + if (!vertex_buffer) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return vertex_buffer->GetData(offset, size, data) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::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 BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::DestroyIndexBuffer(ResourceID id) { + return vertex_buffers_.Destroy(id) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return index_buffer->SetData(offset, size, data) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::ParseError GAPIGL::GetIndexBufferData(ResourceID id, + unsigned int offset, + unsigned int size, + void *data) { + IndexBufferGL *index_buffer = index_buffers_.Get(id); + if (!index_buffer) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + return index_buffer->GetData(offset, size, data) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::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 BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::DestroyVertexStruct(ResourceID id) { + if (id == current_vertex_struct_) validate_streams_ = true; + return vertex_structs_.Destroy(id) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::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::POSITION: + if (semantic_index != 0) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + break; + case vertex_struct::NORMAL: + if (semantic_index != 0) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + break; + case vertex_struct::COLOR: + if (semantic_index >= 2) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + break; + case vertex_struct::TEX_COORD: + if (semantic_index >= 8) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + 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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + vertex_struct->SetInput(input_index, vertex_buffer_id, offset, stride, type, + semantic, semantic_index); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::SetVertexStruct(ResourceID id) { + current_vertex_struct_ = id; + validate_streams_ = true; + return BufferSyncInterface::PARSE_NO_ERROR; +} + +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(GAPIInterface::PrimitiveType primitive_type, + GLenum *gl_mode, + unsigned int *count) { + switch (primitive_type) { + case GAPIInterface::POINTS: + *gl_mode = GL_POINTS; + break; + case GAPIInterface::LINES: + *gl_mode = GL_LINES; + *count *= 2; + break; + case GAPIInterface::LINE_STRIPS: + *gl_mode = GL_LINE_STRIP; + ++*count; + break; + case GAPIInterface::TRIANGLES: + *gl_mode = GL_TRIANGLES; + *count *= 3; + break; + case GAPIInterface::TRIANGLE_STRIPS: + *gl_mode = GL_TRIANGLE_STRIP; + *count += 2; + break; + case GAPIInterface::TRIANGLE_FANS: + *gl_mode = GL_TRIANGLE_FAN; + *count += 2; + break; + default: + LOG(FATAL) << "Invalid primitive type"; + break; + } +} + +} // anonymous namespace + +BufferSyncInterface::ParseError GAPIGL::Draw(PrimitiveType primitive_type, + unsigned int first, + unsigned int count) { + if (validate_effect_ && !ValidateEffect()) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + DCHECK(current_effect_); + if (validate_streams_ && !ValidateStreams()) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + GLenum gl_mode = GL_POINTS; + PrimitiveTypeToGL(primitive_type, &gl_mode, &count); + if (first + count > max_vertices_) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + glDrawArrays(gl_mode, first, count); + CHECK_GL_ERROR(); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::DrawIndexed( + 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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + if (validate_effect_ && !ValidateEffect()) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + DCHECK(current_effect_); + if (validate_streams_ && !ValidateStreams()) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + if ((min_index >= max_vertices_) || (max_index > max_vertices_)) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + 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::INDEX_32BIT) ? + GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + GLuint index_size = (index_buffer->flags() & index_buffer::INDEX_32BIT) ? + sizeof(GLuint) : sizeof(GLushort); // NOLINT + GLuint offset = first * index_size; + if (offset + count * index_size > index_buffer->size()) { + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + } + glDrawRangeElements(gl_mode, min_index, max_index, count, index_type, + OffsetToPtr(offset)); + CHECK_GL_ERROR(); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/gl/geometry_gl.h b/o3d/command_buffer/service/cross/gl/geometry_gl.h new file mode 100644 index 0000000..580f14d --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/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 O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GEOMETRY_GL_H_ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GEOMETRY_GL_H_ + +#include "command_buffer/common/cross/gapi_interface.h" +#include "command_buffer/service/cross/resource.h" +#include "command_buffer/service/cross/gl/gl_utils.h" + +namespace o3d { +namespace command_buffer { + +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; + GLintptr offset; + }; + + // Compiles the vertex declaration into the attribute array. + void Compile(); + + bool dirty_; + AttribDesc attribs_[kMaxAttribs]; + DISALLOW_COPY_AND_ASSIGN(VertexStructGL); +}; + + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GEOMETRY_GL_H_ diff --git a/o3d/command_buffer/service/cross/gl/gl_utils.h b/o3d/command_buffer/service/cross/gl/gl_utils.h new file mode 100644 index 0000000..b41a4cf --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/gl_utils.h @@ -0,0 +1,63 @@ +/* + * 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 implement some useful +// utilities. + +#ifndef O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GL_UTILS_H_ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GL_UTILS_H_ + +#include <build/build_config.h> +#define GL_GLEXT_PROTOTYPES +#if defined(OS_WIN) +#include <GL/gl.h> +#elif defined(OS_MACOSX) +#include <OpenGL/OpenGL.h> +#include <AGL/agl.h> +#elif defined(OS_LINUX) +#include <GL/gl.h> +#endif // defined(PLATFORM) +#include <Cg/cg.h> +#include <Cg/cgGL.h> + +// 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 // O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_GL_UTILS_H_ diff --git a/o3d/command_buffer/service/cross/gl/sampler_gl.cc b/o3d/command_buffer/service/cross/gl/sampler_gl.cc new file mode 100644 index 0000000..2262e6c --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/sampler_gl.cc @@ -0,0 +1,235 @@ +/* + * 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 "command_buffer/service/cross/gl/gapi_gl.h" +#include "command_buffer/service/cross/gl/sampler_gl.h" + +namespace o3d { +namespace command_buffer { + +namespace { + +// Gets the GL enum corresponding to an addressing mode. +GLenum GLAddressMode(sampler::AddressingMode o3d_mode) { + switch (o3d_mode) { + case sampler::WRAP: + return GL_REPEAT; + case sampler::MIRROR_REPEAT: + return GL_MIRRORED_REPEAT; + case sampler::CLAMP_TO_EDGE: + return GL_CLAMP_TO_EDGE; + case sampler::CLAMP_TO_BORDER: + 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::POINT: + if (mip_filter == sampler::NONE) + return GL_NEAREST; + else if (mip_filter == sampler::POINT) + return GL_NEAREST_MIPMAP_NEAREST; + else if (mip_filter == sampler::LINEAR) + return GL_NEAREST_MIPMAP_LINEAR; + case sampler::LINEAR: + if (mip_filter == sampler::NONE) + return GL_LINEAR; + else if (mip_filter == sampler::POINT) + return GL_LINEAR_MIPMAP_NEAREST; + else if (mip_filter == sampler::LINEAR) + 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::POINT: + return GL_NEAREST; + case sampler::LINEAR: + 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::TEXTURE_2D: + return GL_TEXTURE_2D; + case texture::TEXTURE_3D: + return GL_TEXTURE_3D; + case texture::TEXTURE_CUBE: + return GL_TEXTURE_CUBE_MAP; + } +} + +} // anonymous namespace + +SamplerGL::SamplerGL() + : texture_id_(kInvalidResource), + gl_texture_(0) { + SetStates(sampler::CLAMP_TO_EDGE, + sampler::CLAMP_TO_EDGE, + sampler::CLAMP_TO_EDGE, + sampler::LINEAR, + sampler::LINEAR, + sampler::POINT, + 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::NONE); + DCHECK_NE(min_filter, sampler::NONE); + DCHECK_GT(max_anisotropy, 0); + 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; +} + +BufferSyncInterface::ParseError GAPIGL::CreateSampler( + ResourceID id) { + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + samplers_.Assign(id, new SamplerGL()); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +// Destroys the Sampler resource. +BufferSyncInterface::ParseError GAPIGL::DestroySampler(ResourceID id) { + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + return samplers_.Destroy(id) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +BufferSyncInterface::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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // 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 BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::SetSamplerBorderColor( + ResourceID id, + const RGBA &color) { + SamplerGL *sampler = samplers_.Get(id); + if (!sampler) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + sampler->SetBorderColor(color); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +BufferSyncInterface::ParseError GAPIGL::SetSamplerTexture( + ResourceID id, + ResourceID texture_id) { + SamplerGL *sampler = samplers_.Get(id); + if (!sampler) + return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // Dirty effect, because this sampler id may be used. + DirtyEffect(); + sampler->SetTexture(texture_id); + return BufferSyncInterface::PARSE_NO_ERROR; +} + + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/gl/sampler_gl.h b/o3d/command_buffer/service/cross/gl/sampler_gl.h new file mode 100644 index 0000000..dc84b4f --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/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 O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_SAMPLER_GL_H_ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_SAMPLER_GL_H_ + +#include "command_buffer/common/cross/gapi_interface.h" +#include "command_buffer/service/cross/gl/gl_utils.h" +#include "command_buffer/service/cross/resource.h" + +namespace o3d { +namespace command_buffer { + +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 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 command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_SAMPLER_GL_H_ diff --git a/o3d/command_buffer/service/cross/gl/states_gl.cc b/o3d/command_buffer/service/cross/gl/states_gl.cc new file mode 100644 index 0000000..4acc96c --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/states_gl.cc @@ -0,0 +1,342 @@ +/* + * 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 "command_buffer/common/cross/cmd_buffer_format.h" +#include "command_buffer/service/cross/gl/gapi_gl.h" + +namespace o3d { +namespace command_buffer { + +namespace { + +GLenum kGLPolygonModes[] = { + GL_POINT, + GL_LINE, + GL_FILL, +}; +COMPILE_ASSERT(GAPIInterface::NUM_POLYGON_MODE == arraysize(kGLPolygonModes), + kGLPolygonModes_does_not_match_GAPIInterface_PolygonMode); + +GLenum kGLComparison[] = { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, +}; +COMPILE_ASSERT(GAPIInterface::NUM_COMPARISON == arraysize(kGLComparison), + kGLComparison_does_not_match_GAPIInterface_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(GAPIInterface::NUM_BLEND_FUNC == arraysize(kGLBlendFunc), + kGLBlendFunc_does_not_match_GAPIInterface_BlendFunc); + +GLenum kGLBlendEq[] = { + GL_FUNC_ADD, + GL_FUNC_SUBTRACT, + GL_FUNC_REVERSE_SUBTRACT, + GL_MIN, + GL_MAX, +}; +COMPILE_ASSERT(GAPIInterface::NUM_BLEND_EQ == arraysize(kGLBlendEq), + kGLBlendEq_does_not_match_GAPIInterface_BlendEq); + +GLenum kGLStencilOp[] = { + GL_KEEP, + GL_ZERO, + GL_REPLACE, + GL_INCR, + GL_DECR, + GL_INVERT, + GL_INCR_WRAP, + GL_DECR_WRAP, +}; +COMPILE_ASSERT(GAPIInterface::NUM_STENCIL_OP == arraysize(kGLStencilOp), + kGLStencilOp_does_not_match_GAPIInterface_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(set_stencil_test::CW ## FIELD::kLength == \ + set_stencil_test::CCW ## FIELD::kLength, \ + CCW ## FIELD ## _length_does_not_match_ ## CW ## FIELD); \ + COMPILE_ASSERT(set_stencil_test::CW ## FIELD::kShift + 16 == \ + set_stencil_test::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) { + namespace cmd = set_stencil_test; + // Sanity check. The value has already been tested in + // GAPIDecoder::DecodeSetStencilTest in gapi_decoder.cc. + DCHECK_EQ(cmd::Unused1::Get(params), 0); + // Check that the bitmask get cannot generate values outside of the allowed + // range. + COMPILE_ASSERT(cmd::CWFunc::kMask < GAPIInterface::NUM_COMPARISON, + set_stencil_test_CWFunc_may_produce_invalid_values); + *func = kGLComparison[cmd::CWFunc::Get(params)]; + + COMPILE_ASSERT(cmd::CWPassOp::kMask < GAPIInterface::NUM_STENCIL_OP, + set_stencil_test_CWPassOp_may_produce_invalid_values); + *pass = kGLStencilOp[cmd::CWPassOp::Get(params)]; + + COMPILE_ASSERT(cmd::CWFailOp::kMask < GAPIInterface::NUM_STENCIL_OP, + set_stencil_test_CWFailOp_may_produce_invalid_values); + *fail = kGLStencilOp[cmd::CWFailOp::Get(params)]; + + COMPILE_ASSERT(cmd::CWZFailOp::kMask < GAPIInterface::NUM_STENCIL_OP, + set_stencil_test_CWZFailOp_may_produce_invalid_values); + *zfail = kGLStencilOp[cmd::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(PolygonMode fill_mode, + FaceCullMode cull_mode) { + DCHECK_LT(fill_mode, NUM_POLYGON_MODE); + glPolygonMode(GL_FRONT_AND_BACK, kGLPolygonModes[fill_mode]); + DCHECK_LT(cull_mode, NUM_FACE_CULL_MODE); + switch (cull_mode) { + case CULL_CW: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + break; + case CULL_CCW: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + break; + default: + glDisable(GL_CULL_FACE); + break; + } +} + +void GAPIGL::SetAlphaTest(bool enable, + float reference, + Comparison comp) { + DCHECK_LT(comp, NUM_COMPARISON); + if (enable) { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(kGLComparison[comp], reference); + } else { + glDisable(GL_ALPHA_TEST); + } +} + +void GAPIGL::SetDepthTest(bool enable, + bool write_enable, + Comparison comp) { + DCHECK_LT(comp, NUM_COMPARISON); + 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, NUM_BLEND_EQ); + DCHECK_LT(color_src_func, NUM_BLEND_FUNC); + DCHECK_LT(color_dst_func, NUM_BLEND_FUNC); + DCHECK_LT(alpha_eq, NUM_BLEND_EQ); + DCHECK_LT(alpha_src_func, NUM_BLEND_FUNC); + DCHECK_LT(alpha_dst_func, NUM_BLEND_FUNC); + 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 command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/gl/texture_gl.cc b/o3d/command_buffer/service/cross/gl/texture_gl.cc new file mode 100644 index 0000000..9155dcc --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/texture_gl.cc @@ -0,0 +1,678 @@ +/* + * 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 "command_buffer/service/cross/gl/gapi_gl.h" +#include "command_buffer/service/cross/gl/texture_gl.h" + +namespace o3d { +namespace command_buffer { + +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::XRGB8: + *internal_format = GL_RGB; + *gl_format = GL_BGRA; + *gl_type = GL_UNSIGNED_BYTE; + return true; + case texture::ARGB8: + *internal_format = GL_RGBA; + *gl_format = GL_BGRA; + *gl_type = GL_UNSIGNED_BYTE; + return true; + case texture::ABGR16F: + *internal_format = GL_RGBA16F_ARB; + *gl_format = GL_RGBA; + *gl_type = GL_HALF_FLOAT_ARB; + return true; + case texture::DXT1: + *internal_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + *gl_format = 0; + *gl_type = 0; + return true; + default: + return false; + } +} + +// 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) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + DCHECK_GT(levels, 0); + 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; + if (!gl_format) { + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, width, height, 1, 0); + unsigned int size = GetMipLevelSize(mip_info); + buffer.reset(new unsigned char[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, NULL); + } 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, 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; +} + +Texture3DGL *Texture3DGL::Create(unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + DCHECK_GT(depth, 0); + DCHECK_GT(levels, 0); + 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; + if (!gl_format) { + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, width, height, depth, 0); + unsigned int size = GetMipLevelSize(mip_info); + buffer.reset(new unsigned char[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, NULL); + } 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, 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; +} + +TextureCubeGL *TextureCubeGL::Create(unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags) { + DCHECK_GT(side, 0); + DCHECK_GT(levels, 0); + 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; + if (!gl_format) { + MipLevelInfo mip_info; + MakeMipLevelInfo(&mip_info, format, side, side, 1, 0); + unsigned int size = GetMipLevelSize(mip_info); + buffer.reset(new unsigned char[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, NULL); + } + } 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, 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::FACE_POSITIVE_X == + GL_TEXTURE_CUBE_MAP_POSITIVE_X, POSITIVE_X_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::FACE_NEGATIVE_X == + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, NEGATIVE_X_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::FACE_POSITIVE_Y == + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, POSITIVE_Y_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::FACE_NEGATIVE_Y == + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, NEGATIVE_Y_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::FACE_POSITIVE_Z == + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, POSITIVE_Z_ENUMS_DO_NOT_MATCH); +COMPILE_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_X + texture::FACE_NEGATIVE_Z == + 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; +} + +// GAPIGL functions. + +// Destroys a texture resource. +BufferSyncInterface::ParseError GAPIGL::DestroyTexture(ResourceID id) { + // Dirty effect, because this texture id may be used. + DirtyEffect(); + return textures_.Destroy(id) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +// Creates a 2D texture resource. +BufferSyncInterface::ParseError GAPIGL::CreateTexture2D( + ResourceID id, + unsigned int width, + unsigned int height, + unsigned int levels, + texture::Format format, + unsigned int flags) { + Texture2DGL *texture = Texture2DGL::Create(width, height, levels, format, + flags); + if (!texture) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // Dirty effect, because this texture id may be used. + DirtyEffect(); + textures_.Assign(id, texture); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +// Creates a 3D texture resource. +BufferSyncInterface::ParseError GAPIGL::CreateTexture3D( + ResourceID id, + unsigned int width, + unsigned int height, + unsigned int depth, + unsigned int levels, + texture::Format format, + unsigned int flags) { + Texture3DGL *texture = Texture3DGL::Create(width, height, depth, levels, + format, flags); + if (!texture) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // Dirty effect, because this texture id may be used. + DirtyEffect(); + textures_.Assign(id, texture); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +// Creates a cube map texture resource. +BufferSyncInterface::ParseError GAPIGL::CreateTextureCube( + ResourceID id, + unsigned int side, + unsigned int levels, + texture::Format format, + unsigned int flags) { + TextureCubeGL *texture = TextureCubeGL::Create(side, levels, format, flags); + if (!texture) return BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + // Dirty effect, because this texture id may be used. + DirtyEffect(); + textures_.Assign(id, texture); + return BufferSyncInterface::PARSE_NO_ERROR; +} + +// Copies the data into a texture resource. +BufferSyncInterface::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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + 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) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +// Copies the data from a texture resource. +BufferSyncInterface::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 BufferSyncInterface::PARSE_INVALID_ARGUMENTS; + 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) ? + BufferSyncInterface::PARSE_NO_ERROR : + BufferSyncInterface::PARSE_INVALID_ARGUMENTS; +} + +} // namespace command_buffer +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/gl/texture_gl.h b/o3d/command_buffer/service/cross/gl/texture_gl.h new file mode 100644 index 0000000..bc6d9c2 --- /dev/null +++ b/o3d/command_buffer/service/cross/gl/texture_gl.h @@ -0,0 +1,223 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_TEXTURE_GL_H_ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_TEXTURE_GL_H_ + +#include "command_buffer/common/cross/gapi_interface.h" +#include "command_buffer/service/cross/gl/gl_utils.h" +#include "command_buffer/service/cross/resource.h" +#include "command_buffer/service/cross/texture_utils.h" + +namespace o3d { +namespace command_buffer { + +// 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, + unsigned int flags, + GLuint gl_texture) + : Texture(type, levels, format, 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; + + 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, + unsigned int flags, + unsigned int width, + unsigned int height, + GLuint gl_texture) + : TextureGL(texture::TEXTURE_2D, levels, format, 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); + + // 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); + + 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, + unsigned int flags, + unsigned int width, + unsigned int height, + unsigned int depth, + GLuint gl_texture) + : TextureGL(texture::TEXTURE_2D, levels, format, 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); + + // 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); + + 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, + unsigned int flags, + unsigned int side, + GLuint gl_texture) + : TextureGL(texture::TEXTURE_CUBE, levels, format, 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); + + // 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); + + private: + unsigned int side_; + DISALLOW_COPY_AND_ASSIGN(TextureCubeGL); +}; + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_GL_TEXTURE_GL_H_ diff --git a/o3d/command_buffer/service/cross/mocks.h b/o3d/command_buffer/service/cross/mocks.h new file mode 100644 index 0000000..cb245c0 --- /dev/null +++ b/o3d/command_buffer/service/cross/mocks.h @@ -0,0 +1,149 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_MOCKS_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_MOCKS_H__ + +#include <vector> +#include "gmock/gmock.h" +#include "command_buffer/service/cross/cmd_parser.h" +#include "command_buffer/service/cross/cmd_buffer_engine.h" + +namespace o3d { +namespace command_buffer { + +// Mocks an AsyncAPIInterface, using GMock. +class AsyncAPIMock : public AsyncAPIInterface { + public: + AsyncAPIMock() { + testing::DefaultValue<BufferSyncInterface::ParseError>::Set( + BufferSyncInterface::PARSE_NO_ERROR); + } + + // Predicate that matches args passed to DoCommand, by looking at the values. + class IsArgs { + public: + IsArgs(unsigned int arg_count, CommandBufferEntry *args) + : arg_count_(arg_count), + args_(args) { } + + bool operator() (CommandBufferEntry *args) const { + 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, BufferSyncInterface::ParseError( + unsigned int command, + unsigned int arg_count, + CommandBufferEntry *args)); + + // Sets the engine, to forward SET_TOKEN 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, + CommandBufferEntry *args) { + DCHECK(engine_); + DCHECK_EQ(1, command); + DCHECK_EQ(1, arg_count); + engine_->set_token(args[0].value_uint32); + } + private: + CommandBufferEngine *engine_; +}; + +class RPCProcessMock : public RPCProcessInterface { + public: + RPCProcessMock() + : would_have_blocked_(false), + message_count_(0) { + ON_CALL(*this, ProcessMessage()).WillByDefault( + testing::Invoke(this, &RPCProcessMock::DefaultProcessMessage)); + ON_CALL(*this, HasMessage()).WillByDefault( + testing::Invoke(this, &RPCProcessMock::DefaultHasMessage)); + } + MOCK_METHOD0(ProcessMessage, bool()); + MOCK_METHOD0(HasMessage, bool()); + + void Reset() { + would_have_blocked_ = false; + message_count_ = 0; + } + + bool DefaultProcessMessage() { + if (message_count_ > 0) { + --message_count_; + } else { + would_have_blocked_ = true; + } + return true; + } + + bool DefaultHasMessage() { + return message_count_ > 0; + } + + bool AddMessage() { + ++message_count_; + } + + bool would_have_blocked() { return would_have_blocked_; } + void set_would_have_blocked(bool would_have_blocked) { + would_have_blocked_ = would_have_blocked; + } + + unsigned int message_count() { return message_count_; } + void set_message_count(unsigned int count) { message_count_ = count; } + private: + bool would_have_blocked_; + unsigned int message_count_; +}; + + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_MOCKS_H__ diff --git a/o3d/command_buffer/service/cross/plugin.cc b/o3d/command_buffer/service/cross/plugin.cc new file mode 100644 index 0000000..651713d --- /dev/null +++ b/o3d/command_buffer/service/cross/plugin.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 command renderer service (renderer) plug-in. +// NOTE: this is only implemented on Windows currently. +// TODO: other platforms. + +#include <npupp.h> +#include <build/build_config.h> +#ifdef OS_WIN +#include <windows.h> +#endif + +#include "command_buffer/common/cross/gapi_interface.h" +#include "command_buffer/common/cross/rpc_imc.h" +#include "command_buffer/service/cross/buffer_rpc.h" +#include "command_buffer/service/cross/cmd_buffer_engine.h" +#include "command_buffer/service/cross/gapi_decoder.h" +#ifdef OS_WIN +#include "command_buffer/service/win/d3d9/gapi_d3d9.h" +#endif +#include "native_client/service_runtime/nrd_xfer_lib/nrd_all_modules.h" +#include "tools/idlglue/ng/static_glue/npapi/npn_api.h" + +namespace o3d { +namespace command_buffer { + +// The Plugin class implements the plug-in instance. It derives from NPObject +// to be the scriptable object as well. +class Plugin : public NPObject { + public: + // Sets the window used by the plug-in. + NPError SetWindow(NPWindow *window); + + // Gets the NPClass representing the NPAPI entrypoints to the object. + static NPClass *GetNPClass() { + return &class_; + } + + private: + explicit Plugin(NPP npp); + ~Plugin(); + + // Creates the renderer using the IMC socket. This is called from Javascript + // using the create() function. + void Create(nacl::HtpHandle socket); + + // Destroys the renderer. This is called from Javascript using the destroy() + // function. + void Destroy(); + + // NPAPI bindings. + static NPObject *Allocate(NPP npp, NPClass *npclass); + static void Deallocate(NPObject *object); + static bool HasMethod(NPObject *header, NPIdentifier name); + static bool Invoke(NPObject *header, NPIdentifier name, + const NPVariant *args, uint32_t argCount, + NPVariant *result); + static bool InvokeDefault(NPObject *header, const NPVariant *args, + uint32_t argCount, NPVariant *result); + static bool HasProperty(NPObject *header, NPIdentifier name); + static bool GetProperty(NPObject *header, NPIdentifier name, + NPVariant *variant); + static bool SetProperty(NPObject *header, NPIdentifier name, + const NPVariant *variant); + static bool Enumerate(NPObject *header, NPIdentifier **value, + uint32_t *count); + +#ifdef OS_WIN + static DWORD WINAPI ThreadMain(void *param) { + static_cast<Plugin *>(param)->DoThread(); + return 0; + } +#endif + // Executes the main renderer thread. + void DoThread(); + + NPP npp_; + static NPClass class_; + NPIdentifier create_id_; + NPIdentifier destroy_id_; + NPIdentifier handle_id_; + +#ifdef OS_WIN + HWND hwnd_; + HANDLE thread_; + DWORD thread_id_; +#endif + + nacl::HtpHandle handle_; + scoped_ptr<GAPIInterface> gapi_; +}; + + +Plugin::Plugin(NPP npp) + : npp_(npp), +#ifdef OS_WIN + hwnd_(NULL), + thread_(NULL), + thread_id_(0), +#endif + handle_(nacl::kInvalidHtpHandle) { + const char *names[3] = {"create", "destroy", "handle"}; + NPIdentifier ids[3]; + NPN_GetStringIdentifiers(names, 3, ids); + create_id_ = ids[0]; + destroy_id_ = ids[1]; + handle_id_ = ids[2]; +} + +Plugin::~Plugin() { + if (gapi_.get()) Destroy(); +} + +NPError Plugin::SetWindow(NPWindow *window) { +#ifdef OS_WIN + HWND hWnd = window ? static_cast<HWND>(window->window) : NULL; + hwnd_ = hWnd; + return NPERR_NO_ERROR; +#endif // OS_WIN +} + +// Creates the renderer. This spawns a thread that answers requests (the D3D +// context is created in that other thread, so that we don't need to enable +// multi-threading on it). +void Plugin::Create(nacl::HtpHandle handle) { + if (gapi_.get()) return; + handle_ = handle; +#ifdef OS_WIN + if (!hwnd_) return; + GAPID3D9 *gapi_d3d = new GAPID3D9; + gapi_d3d->set_hwnd(hwnd_); + gapi_.reset(gapi_d3d); + // TODO: use chrome/base threads. + thread_ = ::CreateThread(NULL, 0, ThreadMain, this, 0, &thread_id_); +#endif +} + +// Destroys the renderer. This terminates the renderer thread, and waits until +// it is finished. +void Plugin::Destroy() { + if (!gapi_.get()) return; +#ifdef OS_WIN + ::PostThreadMessage(thread_id_, WM_USER, 0, 0); + ::WaitForSingleObject(thread_, INFINITE); + ::CloseHandle(thread_); +#endif + gapi_.reset(NULL); +} + +// Executes the main renderer thread: answers requests, executes commands. +void Plugin::DoThread() { + scoped_ptr<GAPIDecoder> decoder(new GAPIDecoder(gapi_.get())); + scoped_ptr<CommandBufferEngine> engine( + new CommandBufferEngine(decoder.get())); + decoder->set_engine(engine.get()); + + IMCMessageProcessor processor(handle_, engine->rpc_impl()); + engine->set_process_interface(&processor); + IMCSender sender(handle_); + engine->set_client_rpc(&sender); + + gapi_->Initialize(); + while (true) { + bool done = false; +#ifdef OS_WIN + MSG msg; + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + if (msg.message == WM_USER) { + done = true; + break; + } + } +#endif + if (done) break; + // NOTE: DoWork will block when there is nothing to do. This can be an + // issue at termination if the browser tries to kill the plug-in before the + // NaCl module, because then this thread won't terminate, and it will block + // the main (browser) thread. Workaround: kill the NaCl module (kill the + // sel_ldr window). + // TODO: Fix this. It needs select()/poll() or a timeout in the + // IMC library, and that doesn't exist currently. We could use non-blocking, + // e.g. HasWork() and sleep if there is nothing to do, but that would + // translate into unacceptable latencies - 10ms per call. + if (!engine->DoWork()) break; + } + gapi_->Destroy(); +} + +NPClass Plugin::class_ = { + NP_CLASS_STRUCT_VERSION, + Plugin::Allocate, + Plugin::Deallocate, + 0, + Plugin::HasMethod, + Plugin::Invoke, + 0, + Plugin::HasProperty, + Plugin::GetProperty, + Plugin::SetProperty, + 0, + Plugin::Enumerate, +}; + +NPObject *Plugin::Allocate(NPP npp, NPClass *npclass) { + return new Plugin(npp); +} + +void Plugin::Deallocate(NPObject *object) { + delete static_cast<Plugin *>(object); +} + +bool Plugin::HasMethod(NPObject *header, NPIdentifier name) { + Plugin *plugin = static_cast<Plugin *>(header); + // 2 methods supported: create(handle) and destroy(). + return (name == plugin->create_id_ || + name == plugin->destroy_id_); +} + +bool Plugin::Invoke(NPObject *header, NPIdentifier name, + const NPVariant *args, uint32_t argCount, + NPVariant *result) { + Plugin *plugin = static_cast<Plugin *>(header); + VOID_TO_NPVARIANT(*result); + if (name == plugin->create_id_ && argCount == 1 && + NPVARIANT_IS_OBJECT(args[0])) { + // create(handle) was called. + // + // Temporary ugly hack: the NPObject is a wrapper around a HtpHandle, but + // to get that handle we need to get the "handle" property on it which is a + // string that represents the address in memory of that HtpHandle. + NPObject *object = NPVARIANT_TO_OBJECT(args[0]); + + NPVariant handle_prop; + bool result = NPN_GetProperty(plugin->npp_, object, plugin->handle_id_, + &handle_prop); + if (!result || !NPVARIANT_IS_STRING(handle_prop)) + return false; + String handle_string(NPVARIANT_TO_STRING(handle_prop).utf8characters, + NPVARIANT_TO_STRING(handle_prop).utf8length); + intptr_t handle_value = strtol(handle_string.c_str(), NULL, 0); + nacl::HtpHandle handle = reinterpret_cast<nacl::HtpHandle>(handle_value); + plugin->Create(handle); + return true; + } else if (name == plugin->destroy_id_ && argCount == 0) { + // destroy() was called. + plugin->Destroy(); + return true; + } else { + return false; + } +} + +bool Plugin::InvokeDefault(NPObject *header, const NPVariant *args, + uint32_t argCount, NPVariant *result) { + return false; +} + +bool Plugin::HasProperty(NPObject *header, NPIdentifier name) { + return false; +} + +bool Plugin::GetProperty(NPObject *header, NPIdentifier name, + NPVariant *variant) { + return false; +} + +bool Plugin::SetProperty(NPObject *header, NPIdentifier name, + const NPVariant *variant) { + return false; +} + +bool Plugin::Enumerate(NPObject *header, NPIdentifier **value, + uint32_t *count) { + Plugin *plugin = static_cast<Plugin *>(header); + *count = 2; + NPIdentifier *ids = static_cast<NPIdentifier *>( + NPN_MemAlloc(*count * sizeof(NPIdentifier))); + ids[0] = plugin->create_id_; + ids[1] = plugin->destroy_id_; + *value = ids; + return true; +} + +} // namespace command_buffer +} // namespace o3d + +using o3d::command_buffer::Plugin; + +extern "C" { +// NPAPI entry points. + +char *NP_GetMIMEDescription(void) { + return "application/vnd.cmdbuf::CommandBuffer MIME"; +} + +NPError OSCALL NP_Initialize(NPNetscapeFuncs *browserFuncs) { + NPError retval = InitializeNPNApi(browserFuncs); + if (retval != NPERR_NO_ERROR) return retval; + return NPERR_NO_ERROR; +} + +NPError OSCALL NP_GetEntryPoints(NPPluginFuncs *pluginFuncs) { + pluginFuncs->version = 11; + pluginFuncs->size = sizeof(pluginFuncs); + pluginFuncs->newp = NPP_New; + pluginFuncs->destroy = NPP_Destroy; + pluginFuncs->setwindow = NPP_SetWindow; + pluginFuncs->newstream = NPP_NewStream; + pluginFuncs->destroystream = NPP_DestroyStream; + pluginFuncs->asfile = NPP_StreamAsFile; + pluginFuncs->writeready = NPP_WriteReady; + pluginFuncs->write = NPP_Write; + pluginFuncs->print = NPP_Print; + pluginFuncs->event = NPP_HandleEvent; + pluginFuncs->urlnotify = NPP_URLNotify; + pluginFuncs->getvalue = NPP_GetValue; + pluginFuncs->setvalue = NPP_SetValue; + + return NPERR_NO_ERROR; +} + +NPError OSCALL NP_Shutdown(void) { + return NPERR_NO_ERROR; +} + +// Creates a plugin instance. +NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, + char *argn[], char *argv[], NPSavedData *saved) { + NPObject *object = NPN_CreateObject(instance, Plugin::GetNPClass()); + if (object == NULL) { + return NPERR_OUT_OF_MEMORY_ERROR; + } + instance->pdata = object; + return NPERR_NO_ERROR; +} + +// Destroys a plugin instance. +NPError NPP_Destroy(NPP instance, NPSavedData **save) { + Plugin *obj = static_cast<Plugin*>(instance->pdata); + if (obj) { + obj->SetWindow(NULL); + NPN_ReleaseObject(obj); + instance->pdata = NULL; + } + + return NPERR_NO_ERROR; +} + +// Sets the window used by the plugin instance. +NPError NPP_SetWindow(NPP instance, NPWindow *window) { + Plugin *obj = static_cast<Plugin*>(instance->pdata); + obj->SetWindow(window); + return NPERR_NO_ERROR; +} + +// Gets the scriptable object for the plug-in instance. +NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) { + switch (variable) { + case NPPVpluginScriptableNPObject: { + void **v = static_cast<void **>(value); + Plugin *obj = static_cast<Plugin*>(instance->pdata); + NPN_RetainObject(obj); + *v = obj; + return NPERR_NO_ERROR; + } + default: + break; + } + return NPERR_GENERIC_ERROR; +} + +NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, + NPBool seekable, uint16 *stype) { + return NPERR_NO_ERROR; +} + +NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) { + return NPERR_NO_ERROR; +} + +int32 NPP_WriteReady(NPP instance, NPStream *stream) { + return 4096; +} + +int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, + void *buffer) { + return len; +} + +void NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname) { +} + +void NPP_Print(NPP instance, NPPrint *platformPrint) { +} + +int16 NPP_HandleEvent(NPP instance, void *event) { + return 0; +} + +void NPP_URLNotify(NPP instance, const char *url, NPReason reason, + void *notifyData) { +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) { + return NPERR_GENERIC_ERROR; +} +} // extern "C" diff --git a/o3d/command_buffer/service/cross/resource.cc b/o3d/command_buffer/service/cross/resource.cc new file mode 100644 index 0000000..00a9f86 --- /dev/null +++ b/o3d/command_buffer/service/cross/resource.cc @@ -0,0 +1,102 @@ +/* + * 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 "command_buffer/service/cross/resource.h" + +namespace o3d { +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 +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/resource.h b/o3d/command_buffer/service/cross/resource.h new file mode 100644 index 0000000..5c6e6ed --- /dev/null +++ b/o3d/command_buffer/service/cross/resource.h @@ -0,0 +1,249 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_RESOURCE_H__ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_RESOURCE_H__ + +#include <vector> +#include "base/scoped_ptr.h" +#include "core/cross/types.h" +#include "command_buffer/common/cross/resource.h" + +namespace o3d { +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, + unsigned int flags) + : type_(type), + levels_(levels), + format_(format), + 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 the number of mipmap levels in the texture. + unsigned int levels() const { return levels_; } + private: + texture::Type type_; + unsigned int levels_; + texture::Format format_; + unsigned int flags_; + DISALLOW_COPY_AND_ASSIGN(Texture); +}; + +// 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 down_cast<T*>(container_.Get(id)); + } + private: + ResourceMapBase container_; +}; + +} // namespace command_buffer +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_RESOURCE_H__ diff --git a/o3d/command_buffer/service/cross/resource_test.cc b/o3d/command_buffer/service/cross/resource_test.cc new file mode 100644 index 0000000..e8fedb0 --- /dev/null +++ b/o3d/command_buffer/service/cross/resource_test.cc @@ -0,0 +1,128 @@ +/* + * 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 "tests/common/win/testing_common.h" +#include "command_buffer/service/cross/resource.h" + +namespace o3d { +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 +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/texture_utils.cc b/o3d/command_buffer/service/cross/texture_utils.cc new file mode 100644 index 0000000..926f6db --- /dev/null +++ b/o3d/command_buffer/service/cross/texture_utils.cc @@ -0,0 +1,104 @@ +/* + * 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 <stdlib.h> +#include "command_buffer/service/cross/texture_utils.h" + +namespace o3d { +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 +} // namespace o3d diff --git a/o3d/command_buffer/service/cross/texture_utils.h b/o3d/command_buffer/service/cross/texture_utils.h new file mode 100644 index 0000000..aa186b5 --- /dev/null +++ b/o3d/command_buffer/service/cross/texture_utils.h @@ -0,0 +1,158 @@ +/* + * 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 O3D_COMMAND_BUFFER_SERVICE_CROSS_TEXTURE_UTILS_H_ +#define O3D_COMMAND_BUFFER_SERVICE_CROSS_TEXTURE_UTILS_H_ + +#include "command_buffer/common/cross/logging.h" +#include "command_buffer/common/cross/resource.h" + +namespace o3d { +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, 0); + DCHECK_GT(block, 0); + 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 +} // namespace o3d + +#endif // O3D_COMMAND_BUFFER_SERVICE_CROSS_TEXTURE_UTILS_H_ |