mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
336 lines
10 KiB
C++
336 lines
10 KiB
C++
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "gpu/command_buffer/service/common_decoder.h"
|
|
|
|
#include "base/numerics/safe_math.h"
|
|
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
|
|
|
|
namespace gpu {
|
|
|
|
const CommonDecoder::CommandInfo CommonDecoder::command_info[] = {
|
|
#define COMMON_COMMAND_BUFFER_CMD_OP(name) \
|
|
{ \
|
|
&CommonDecoder::Handle##name, cmd::name::kArgFlags, \
|
|
cmd::name::cmd_flags, \
|
|
sizeof(cmd::name) / sizeof(CommandBufferEntry) - 1, \
|
|
} \
|
|
, /* NOLINT */
|
|
COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
|
|
#undef COMMON_COMMAND_BUFFER_CMD_OP
|
|
};
|
|
|
|
|
|
CommonDecoder::Bucket::Bucket() : size_(0) {}
|
|
|
|
CommonDecoder::Bucket::~Bucket() {}
|
|
|
|
void* CommonDecoder::Bucket::GetData(size_t offset, size_t size) const {
|
|
if (OffsetSizeValid(offset, size)) {
|
|
return data_.get() + offset;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CommonDecoder::Bucket::SetSize(size_t size) {
|
|
if (size != size_) {
|
|
data_.reset(size ? new int8[size] : NULL);
|
|
size_ = size;
|
|
memset(data_.get(), 0, size);
|
|
}
|
|
}
|
|
|
|
bool CommonDecoder::Bucket::SetData(
|
|
const void* src, size_t offset, size_t size) {
|
|
if (OffsetSizeValid(offset, size)) {
|
|
memcpy(data_.get() + offset, src, size);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CommonDecoder::Bucket::SetFromString(const char* str) {
|
|
// Strings are passed NULL terminated to distinguish between empty string
|
|
// and no string.
|
|
if (!str) {
|
|
SetSize(0);
|
|
} else {
|
|
size_t size = strlen(str) + 1;
|
|
SetSize(size);
|
|
SetData(str, 0, size);
|
|
}
|
|
}
|
|
|
|
bool CommonDecoder::Bucket::GetAsString(std::string* str) {
|
|
DCHECK(str);
|
|
if (size_ == 0) {
|
|
return false;
|
|
}
|
|
str->assign(GetDataAs<const char*>(0, size_ - 1), size_ - 1);
|
|
return true;
|
|
}
|
|
|
|
bool CommonDecoder::Bucket::GetAsStrings(
|
|
GLsizei* _count, std::vector<char*>* _string, std::vector<GLint>* _length) {
|
|
const size_t kMinBucketSize = sizeof(GLint);
|
|
// Each string has at least |length| in the header and a NUL character.
|
|
const size_t kMinStringSize = sizeof(GLint) + 1;
|
|
const size_t bucket_size = this->size();
|
|
if (bucket_size < kMinBucketSize) {
|
|
return false;
|
|
}
|
|
char* bucket_data = this->GetDataAs<char*>(0, bucket_size);
|
|
GLint* header = reinterpret_cast<GLint*>(bucket_data);
|
|
GLsizei count = static_cast<GLsizei>(header[0]);
|
|
if (count < 0) {
|
|
return false;
|
|
}
|
|
const size_t max_count = (bucket_size - kMinBucketSize) / kMinStringSize;
|
|
if (max_count < static_cast<size_t>(count)) {
|
|
return false;
|
|
}
|
|
GLint* length = header + 1;
|
|
std::vector<char*> strs(count);
|
|
base::CheckedNumeric<size_t> total_size = sizeof(GLint);
|
|
total_size *= count + 1; // Header size.
|
|
if (!total_size.IsValid())
|
|
return false;
|
|
for (GLsizei ii = 0; ii < count; ++ii) {
|
|
strs[ii] = bucket_data + total_size.ValueOrDefault(0);
|
|
total_size += length[ii];
|
|
total_size += 1; // NUL char at the end of each char array.
|
|
if (!total_size.IsValid() || total_size.ValueOrDefault(0) > bucket_size ||
|
|
strs[ii][length[ii]] != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
if (total_size.ValueOrDefault(0) != bucket_size) {
|
|
return false;
|
|
}
|
|
DCHECK(_count && _string && _length);
|
|
*_count = count;
|
|
*_string = strs;
|
|
_length->resize(count);
|
|
for (GLsizei ii = 0; ii < count; ++ii) {
|
|
(*_length)[ii] = length[ii];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CommonDecoder::CommonDecoder() : engine_(NULL) {}
|
|
|
|
CommonDecoder::~CommonDecoder() {}
|
|
|
|
void* CommonDecoder::GetAddressAndCheckSize(unsigned int shm_id,
|
|
unsigned int data_offset,
|
|
unsigned int data_size) {
|
|
CHECK(engine_);
|
|
scoped_refptr<gpu::Buffer> buffer = engine_->GetSharedMemoryBuffer(shm_id);
|
|
if (!buffer.get())
|
|
return NULL;
|
|
return buffer->GetDataAddress(data_offset, data_size);
|
|
}
|
|
|
|
scoped_refptr<gpu::Buffer> CommonDecoder::GetSharedMemoryBuffer(
|
|
unsigned int shm_id) {
|
|
return engine_->GetSharedMemoryBuffer(shm_id);
|
|
}
|
|
|
|
const char* CommonDecoder::GetCommonCommandName(
|
|
cmd::CommandId command_id) const {
|
|
return cmd::GetCommandName(command_id);
|
|
}
|
|
|
|
CommonDecoder::Bucket* CommonDecoder::GetBucket(uint32 bucket_id) const {
|
|
BucketMap::const_iterator iter(buckets_.find(bucket_id));
|
|
return iter != buckets_.end() ? &(*iter->second) : NULL;
|
|
}
|
|
|
|
CommonDecoder::Bucket* CommonDecoder::CreateBucket(uint32 bucket_id) {
|
|
Bucket* bucket = GetBucket(bucket_id);
|
|
if (!bucket) {
|
|
bucket = new Bucket();
|
|
buckets_[bucket_id] = linked_ptr<Bucket>(bucket);
|
|
}
|
|
return bucket;
|
|
}
|
|
|
|
namespace {
|
|
|
|
// Returns the address of the first byte after a struct.
|
|
template <typename T>
|
|
const void* AddressAfterStruct(const T& pod) {
|
|
return reinterpret_cast<const uint8*>(&pod) + sizeof(pod);
|
|
}
|
|
|
|
// Returns the address of the frst byte after the struct.
|
|
template <typename RETURN_TYPE, typename COMMAND_TYPE>
|
|
RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod) {
|
|
return static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod)));
|
|
}
|
|
|
|
} // anonymous namespace.
|
|
|
|
// Decode command with its arguments, and call the corresponding method.
|
|
// Note: args is a pointer to the command buffer. As such, it could be changed
|
|
// by a (malicious) client at any time, so if validation has to happen, it
|
|
// should operate on a copy of them.
|
|
error::Error CommonDecoder::DoCommonCommand(
|
|
unsigned int command,
|
|
unsigned int arg_count,
|
|
const void* cmd_data) {
|
|
if (command < arraysize(command_info)) {
|
|
const CommandInfo& info = command_info[command];
|
|
unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count);
|
|
if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) ||
|
|
(info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) {
|
|
uint32 immediate_data_size =
|
|
(arg_count - info_arg_count) * sizeof(CommandBufferEntry); // NOLINT
|
|
return (this->*info.cmd_handler)(immediate_data_size, cmd_data);
|
|
} else {
|
|
return error::kInvalidArguments;
|
|
}
|
|
}
|
|
return error::kUnknownCommand;
|
|
}
|
|
|
|
error::Error CommonDecoder::HandleNoop(
|
|
uint32 immediate_data_size,
|
|
const void* cmd_data) {
|
|
return error::kNoError;
|
|
}
|
|
|
|
error::Error CommonDecoder::HandleSetToken(
|
|
uint32 immediate_data_size,
|
|
const void* cmd_data) {
|
|
const cmd::SetToken& args = *static_cast<const cmd::SetToken*>(cmd_data);
|
|
engine_->set_token(args.token);
|
|
return error::kNoError;
|
|
}
|
|
|
|
error::Error CommonDecoder::HandleSetBucketSize(
|
|
uint32 immediate_data_size,
|
|
const void* cmd_data) {
|
|
const cmd::SetBucketSize& args =
|
|
*static_cast<const cmd::SetBucketSize*>(cmd_data);
|
|
uint32 bucket_id = args.bucket_id;
|
|
uint32 size = args.size;
|
|
|
|
Bucket* bucket = CreateBucket(bucket_id);
|
|
bucket->SetSize(size);
|
|
return error::kNoError;
|
|
}
|
|
|
|
error::Error CommonDecoder::HandleSetBucketData(
|
|
uint32 immediate_data_size,
|
|
const void* cmd_data) {
|
|
const cmd::SetBucketData& args =
|
|
*static_cast<const cmd::SetBucketData*>(cmd_data);
|
|
uint32 bucket_id = args.bucket_id;
|
|
uint32 offset = args.offset;
|
|
uint32 size = args.size;
|
|
const void* data = GetSharedMemoryAs<const void*>(
|
|
args.shared_memory_id, args.shared_memory_offset, size);
|
|
if (!data) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
Bucket* bucket = GetBucket(bucket_id);
|
|
if (!bucket) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
if (!bucket->SetData(data, offset, size)) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
|
|
return error::kNoError;
|
|
}
|
|
|
|
error::Error CommonDecoder::HandleSetBucketDataImmediate(
|
|
uint32 immediate_data_size,
|
|
const void* cmd_data) {
|
|
const cmd::SetBucketDataImmediate& args =
|
|
*static_cast<const cmd::SetBucketDataImmediate*>(cmd_data);
|
|
const void* data = GetImmediateDataAs<const void*>(args);
|
|
uint32 bucket_id = args.bucket_id;
|
|
uint32 offset = args.offset;
|
|
uint32 size = args.size;
|
|
if (size > immediate_data_size) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
Bucket* bucket = GetBucket(bucket_id);
|
|
if (!bucket) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
if (!bucket->SetData(data, offset, size)) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
return error::kNoError;
|
|
}
|
|
|
|
error::Error CommonDecoder::HandleGetBucketStart(
|
|
uint32 immediate_data_size,
|
|
const void* cmd_data) {
|
|
const cmd::GetBucketStart& args =
|
|
*static_cast<const cmd::GetBucketStart*>(cmd_data);
|
|
uint32 bucket_id = args.bucket_id;
|
|
uint32* result = GetSharedMemoryAs<uint32*>(
|
|
args.result_memory_id, args.result_memory_offset, sizeof(*result));
|
|
int32 data_memory_id = args.data_memory_id;
|
|
uint32 data_memory_offset = args.data_memory_offset;
|
|
uint32 data_memory_size = args.data_memory_size;
|
|
uint8* data = NULL;
|
|
if (data_memory_size != 0 || data_memory_id != 0 || data_memory_offset != 0) {
|
|
data = GetSharedMemoryAs<uint8*>(
|
|
args.data_memory_id, args.data_memory_offset, args.data_memory_size);
|
|
if (!data) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
}
|
|
if (!result) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
// Check that the client initialized the result.
|
|
if (*result != 0) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
Bucket* bucket = GetBucket(bucket_id);
|
|
if (!bucket) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
uint32 bucket_size = bucket->size();
|
|
*result = bucket_size;
|
|
if (data) {
|
|
uint32 size = std::min(data_memory_size, bucket_size);
|
|
memcpy(data, bucket->GetData(0, size), size);
|
|
}
|
|
return error::kNoError;
|
|
}
|
|
|
|
error::Error CommonDecoder::HandleGetBucketData(
|
|
uint32 immediate_data_size,
|
|
const void* cmd_data) {
|
|
const cmd::GetBucketData& args =
|
|
*static_cast<const cmd::GetBucketData*>(cmd_data);
|
|
uint32 bucket_id = args.bucket_id;
|
|
uint32 offset = args.offset;
|
|
uint32 size = args.size;
|
|
void* data = GetSharedMemoryAs<void*>(
|
|
args.shared_memory_id, args.shared_memory_offset, size);
|
|
if (!data) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
Bucket* bucket = GetBucket(bucket_id);
|
|
if (!bucket) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
const void* src = bucket->GetData(offset, size);
|
|
if (!src) {
|
|
return error::kInvalidArguments;
|
|
}
|
|
memcpy(data, src, size);
|
|
return error::kNoError;
|
|
}
|
|
|
|
} // namespace gpu
|