1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This file contains the implementation of the command parser.
6
7#include "gpu/command_buffer/service/cmd_parser.h"
8
9#include "base/logging.h"
10#include "base/debug/trace_event.h"
11
12namespace gpu {
13
14CommandParser::CommandParser(AsyncAPIInterface* handler)
15    : get_(0),
16      put_(0),
17      buffer_(NULL),
18      entry_count_(0),
19      handler_(handler) {
20}
21
22void CommandParser::SetBuffer(
23    void* shm_address,
24    size_t shm_size,
25    ptrdiff_t offset,
26    size_t size) {
27  // check proper alignments.
28  DCHECK_EQ(0, (reinterpret_cast<intptr_t>(shm_address)) % 4);
29  DCHECK_EQ(0, offset % 4);
30  DCHECK_EQ(0u, size % 4);
31  // check that the command buffer fits into the memory buffer.
32  DCHECK_GE(shm_size, offset + size);
33  get_ = 0;
34  put_ = 0;
35  char* buffer_begin = static_cast<char*>(shm_address) + offset;
36  buffer_ = reinterpret_cast<CommandBufferEntry*>(buffer_begin);
37  entry_count_ = size / 4;
38}
39
40// Process one command, reading the header from the command buffer, and
41// forwarding the command index and the arguments to the handler.
42// Note that:
43// - validation needs to happen on a copy of the data (to avoid race
44// conditions). This function only validates the header, leaving the arguments
45// validation to the handler, so it can pass a reference to them.
46// - get_ is modified *after* the command has been executed.
47error::Error CommandParser::ProcessCommands(int num_commands) {
48  int num_entries = put_ < get_ ? entry_count_ - get_ : put_ - get_;
49  int entries_processed = 0;
50
51  error::Error result = handler_->DoCommands(
52      num_commands, buffer_ + get_, num_entries, &entries_processed);
53
54  get_ += entries_processed;
55  if (get_ == entry_count_)
56    get_ = 0;
57
58  return result;
59}
60
61// Processes all the commands, while the buffer is not empty. Stop if an error
62// is encountered.
63error::Error CommandParser::ProcessAllCommands() {
64  while (!IsEmpty()) {
65    error::Error error = ProcessCommands(kParseCommandsSlice);
66    if (error)
67      return error;
68  }
69  return error::kNoError;
70}
71
72// Decode multiple commands, and call the corresponding GL functions.
73// NOTE: buffer is a pointer to the command buffer. As such, it could be
74// changed by a (malicious) client at any time, so if validation has to happen,
75// it should operate on a copy of them.
76error::Error AsyncAPIInterface::DoCommands(unsigned int num_commands,
77                                           const void* buffer,
78                                           int num_entries,
79                                           int* entries_processed) {
80  int commands_to_process = num_commands;
81  error::Error result = error::kNoError;
82  const CommandBufferEntry* cmd_data =
83      static_cast<const CommandBufferEntry*>(buffer);
84  int process_pos = 0;
85
86  while (process_pos < num_entries && result == error::kNoError &&
87         commands_to_process--) {
88    CommandHeader header = cmd_data->value_header;
89    if (header.size == 0) {
90      DVLOG(1) << "Error: zero sized command in command buffer";
91      return error::kInvalidSize;
92    }
93
94    if (static_cast<int>(header.size) + process_pos > num_entries) {
95      DVLOG(1) << "Error: get offset out of bounds";
96      return error::kOutOfBounds;
97    }
98
99    const unsigned int command = header.command;
100    const unsigned int arg_count = header.size - 1;
101
102    result = DoCommand(command, arg_count, cmd_data);
103
104    if (result != error::kDeferCommandUntilLater) {
105      process_pos += header.size;
106      cmd_data += header.size;
107    }
108  }
109
110  if (entries_processed)
111    *entries_processed = process_pos;
112
113  return result;
114}
115
116}  // namespace gpu
117