ppapi_command_buffer_proxy.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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#include "ppapi/proxy/ppapi_command_buffer_proxy.h"
6
7#include "ppapi/proxy/ppapi_messages.h"
8#include "ppapi/proxy/proxy_channel.h"
9#include "ppapi/shared_impl/api_id.h"
10#include "ppapi/shared_impl/host_resource.h"
11
12namespace ppapi {
13namespace proxy {
14
15PpapiCommandBufferProxy::PpapiCommandBufferProxy(
16    const ppapi::HostResource& resource,
17    ProxyChannel* channel)
18    : resource_(resource),
19      channel_(channel) {
20}
21
22PpapiCommandBufferProxy::~PpapiCommandBufferProxy() {
23  // Delete all the locally cached shared memory objects, closing the handle
24  // in this process.
25  for (TransferBufferMap::iterator it = transfer_buffers_.begin();
26       it != transfer_buffers_.end(); ++it) {
27    delete it->second.shared_memory;
28    it->second.shared_memory = NULL;
29  }
30}
31
32void PpapiCommandBufferProxy::ReportChannelError() {
33  if (!channel_error_callback_.is_null()) {
34    channel_error_callback_.Run();
35    channel_error_callback_.Reset();
36  }
37}
38
39int PpapiCommandBufferProxy::GetRouteID() const {
40  NOTIMPLEMENTED();
41  return 0;
42}
43
44bool PpapiCommandBufferProxy::Echo(const base::Closure& callback) {
45  return false;
46}
47
48bool PpapiCommandBufferProxy::ProduceFrontBuffer(const gpu::Mailbox& mailbox) {
49  NOTIMPLEMENTED();
50  return false;
51}
52
53void PpapiCommandBufferProxy::SetChannelErrorCallback(
54    const base::Closure& callback) {
55  channel_error_callback_ = callback;
56}
57
58bool PpapiCommandBufferProxy::Initialize() {
59  return true;
60}
61
62gpu::CommandBuffer::State PpapiCommandBufferProxy::GetState() {
63  // Send will flag state with lost context if IPC fails.
64  if (last_state_.error == gpu::error::kNoError) {
65    gpu::CommandBuffer::State state;
66    bool success = false;
67    if (Send(new PpapiHostMsg_PPBGraphics3D_GetState(
68             ppapi::API_ID_PPB_GRAPHICS_3D, resource_, &state, &success))) {
69      UpdateState(state, success);
70    }
71  }
72
73  return last_state_;
74}
75
76gpu::CommandBuffer::State PpapiCommandBufferProxy::GetLastState() {
77  // Note: The locking command buffer wrapper does not take a global lock before
78  // calling this function.
79  return last_state_;
80}
81
82int32 PpapiCommandBufferProxy::GetLastToken() {
83  // Note: The locking command buffer wrapper does not take a global lock before
84  // calling this function.
85  return last_state_.token;
86}
87
88void PpapiCommandBufferProxy::Flush(int32 put_offset) {
89  if (last_state_.error != gpu::error::kNoError)
90    return;
91
92  IPC::Message* message = new PpapiHostMsg_PPBGraphics3D_AsyncFlush(
93      ppapi::API_ID_PPB_GRAPHICS_3D, resource_, put_offset);
94
95  // Do not let a synchronous flush hold up this message. If this handler is
96  // deferred until after the synchronous flush completes, it will overwrite the
97  // cached last_state_ with out-of-date data.
98  message->set_unblock(true);
99  Send(message);
100}
101
102gpu::CommandBuffer::State PpapiCommandBufferProxy::FlushSync(int32 put_offset,
103                                                   int32 last_known_get) {
104  if (last_known_get == last_state_.get_offset) {
105    // Send will flag state with lost context if IPC fails.
106    if (last_state_.error == gpu::error::kNoError) {
107      gpu::CommandBuffer::State state;
108      bool success = false;
109      if (Send(new PpapiHostMsg_PPBGraphics3D_Flush(
110               ppapi::API_ID_PPB_GRAPHICS_3D, resource_, put_offset,
111              last_known_get, &state, &success))) {
112        UpdateState(state, success);
113      }
114    }
115  } else {
116    Flush(put_offset);
117  }
118  return last_state_;
119}
120
121void PpapiCommandBufferProxy::SetGetBuffer(int32 transfer_buffer_id) {
122  if (last_state_.error == gpu::error::kNoError) {
123    Send(new PpapiHostMsg_PPBGraphics3D_SetGetBuffer(
124         ppapi::API_ID_PPB_GRAPHICS_3D, resource_, transfer_buffer_id));
125  }
126}
127
128void PpapiCommandBufferProxy::SetGetOffset(int32 get_offset) {
129  // Not implemented in proxy.
130  NOTREACHED();
131}
132
133gpu::Buffer PpapiCommandBufferProxy::CreateTransferBuffer(size_t size,
134                                                          int32* id) {
135  *id = -1;
136
137  if (last_state_.error != gpu::error::kNoError)
138    return gpu::Buffer();
139
140  if (!Send(new PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer(
141            ppapi::API_ID_PPB_GRAPHICS_3D, resource_, size, id))) {
142    return gpu::Buffer();
143  }
144
145  if ((*id) <= 0)
146    return gpu::Buffer();
147
148  return GetTransferBuffer(*id);
149}
150
151void PpapiCommandBufferProxy::DestroyTransferBuffer(int32 id) {
152  if (last_state_.error != gpu::error::kNoError)
153    return;
154
155  // Remove the transfer buffer from the client side4 cache.
156  TransferBufferMap::iterator it = transfer_buffers_.find(id);
157
158  if (it != transfer_buffers_.end()) {
159    // Delete the shared memory object, closing the handle in this process.
160    delete it->second.shared_memory;
161
162    transfer_buffers_.erase(it);
163  }
164
165  Send(new PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer(
166      ppapi::API_ID_PPB_GRAPHICS_3D, resource_, id));
167}
168
169gpu::Buffer PpapiCommandBufferProxy::GetTransferBuffer(int32 id) {
170  if (last_state_.error != gpu::error::kNoError)
171    return gpu::Buffer();
172
173  // Check local cache to see if there is already a client side shared memory
174  // object for this id.
175  TransferBufferMap::iterator it = transfer_buffers_.find(id);
176  if (it != transfer_buffers_.end()) {
177    return it->second;
178  }
179
180  // Assuming we are in the renderer process, the service is responsible for
181  // duplicating the handle. This might not be true for NaCl.
182  ppapi::proxy::SerializedHandle handle(
183      ppapi::proxy::SerializedHandle::SHARED_MEMORY);
184  if (!Send(new PpapiHostMsg_PPBGraphics3D_GetTransferBuffer(
185            ppapi::API_ID_PPB_GRAPHICS_3D, resource_, id, &handle))) {
186    return gpu::Buffer();
187  }
188  if (!handle.is_shmem())
189    return gpu::Buffer();
190
191  // Cache the transfer buffer shared memory object client side.
192  scoped_ptr<base::SharedMemory> shared_memory(
193      new base::SharedMemory(handle.shmem(), false));
194
195  // Map the shared memory on demand.
196  if (!shared_memory->memory()) {
197    if (!shared_memory->Map(handle.size())) {
198      return gpu::Buffer();
199    }
200  }
201
202  gpu::Buffer buffer;
203  buffer.ptr = shared_memory->memory();
204  buffer.size = handle.size();
205  buffer.shared_memory = shared_memory.release();
206  transfer_buffers_[id] = buffer;
207
208  return buffer;
209}
210
211void PpapiCommandBufferProxy::SetToken(int32 token) {
212  NOTREACHED();
213}
214
215void PpapiCommandBufferProxy::SetParseError(gpu::error::Error error) {
216  NOTREACHED();
217}
218
219void PpapiCommandBufferProxy::SetContextLostReason(
220    gpu::error::ContextLostReason reason) {
221  NOTREACHED();
222}
223
224uint32 PpapiCommandBufferProxy::InsertSyncPoint() {
225  uint32 sync_point = 0;
226  if (last_state_.error == gpu::error::kNoError) {
227    Send(new PpapiHostMsg_PPBGraphics3D_InsertSyncPoint(
228         ppapi::API_ID_PPB_GRAPHICS_3D, resource_, &sync_point));
229  }
230  return sync_point;
231}
232
233bool PpapiCommandBufferProxy::Send(IPC::Message* msg) {
234  DCHECK(last_state_.error == gpu::error::kNoError);
235
236  if (channel_->Send(msg))
237    return true;
238
239  last_state_.error = gpu::error::kLostContext;
240  return false;
241}
242
243void PpapiCommandBufferProxy::UpdateState(
244    const gpu::CommandBuffer::State& state,
245    bool success) {
246  // Handle wraparound. It works as long as we don't have more than 2B state
247  // updates in flight across which reordering occurs.
248  if (success) {
249    if (state.generation - last_state_.generation < 0x80000000U) {
250      last_state_ = state;
251    }
252  } else {
253    last_state_.error = gpu::error::kLostContext;
254    ++last_state_.generation;
255  }
256}
257
258}  // namespace proxy
259}  // namespace ppapi
260