| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "context.h" |
| #include "gfx_api.h" |
| #include "interpreter.h" |
| #include "memory_manager.h" |
| #include "post_buffer.h" |
| #include "renderer.h" |
| #include "replay_request.h" |
| #include "resource_provider.h" |
| #include "resource_in_memory_cache.h" |
| #include "server_connection.h" |
| #include "stack.h" |
| |
| #include <gapic/log.h> |
| #include <gapic/target.h> |
| |
| #include <cstdlib> |
| #include <sstream> |
| #include <string> |
| |
| namespace gapir { |
| |
| std::unique_ptr<Context> Context::create(const ServerConnection& gazer, |
| ResourceProvider* resourceProvider, |
| MemoryManager* memoryManager) { |
| std::unique_ptr<Context> context(new Context(gazer, resourceProvider, memoryManager)); |
| |
| if (context->initialize()) { |
| return context; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| // TODO: Make the PostBuffer size dynamic? It currently holds 2MB of data. |
| Context::Context(const ServerConnection& gazer, ResourceProvider* resourceProvider, |
| MemoryManager* memoryManager) : |
| mServer(gazer), mResourceProvider(resourceProvider), mMemoryManager(memoryManager), |
| mBoundRenderer(nullptr), |
| mPostBuffer(new PostBuffer(POST_BUFFER_SIZE, [this](const void* address, uint32_t count) { |
| return this->mServer.post(address, count); |
| })) { |
| } |
| |
| Context::~Context() { |
| for (auto it = mRenderers.begin(); it != mRenderers.end(); it++) { |
| delete it->second; |
| } |
| } |
| |
| bool Context::initialize() { |
| mReplayRequest = ReplayRequest::create(mServer, mResourceProvider, mMemoryManager); |
| if (mReplayRequest == nullptr) { |
| return false; |
| } |
| |
| if (!mMemoryManager->setVolatileMemory(mReplayRequest->getVolatileMemorySize())) { |
| GAPID_WARNING("Setting the volatile memory size failed (size: %u)", |
| mReplayRequest->getVolatileMemorySize()); |
| return false; |
| } |
| |
| mMemoryManager->setConstantMemory(mReplayRequest->getConstantMemory()); |
| |
| return true; |
| } |
| |
| |
| void Context::prefetch(ResourceInMemoryCache* cache) const { |
| auto cacheSize = static_cast<uint32_t>( |
| static_cast<uint8_t*>(mMemoryManager->getVolatileAddress()) - |
| static_cast<uint8_t*>(mMemoryManager->getBaseAddress())); |
| cache->resize(cacheSize); |
| |
| auto resources = mReplayRequest->getResources(); |
| if (resources.size() > 0) { |
| GAPID_INFO("Prefetching resources..."); |
| mResourceProvider->prefetch(&resources[0], resources.size(), mServer, |
| mMemoryManager->getVolatileAddress(), |
| mReplayRequest->getVolatileMemorySize()); |
| GAPID_INFO("Prefetching ready"); |
| } |
| } |
| |
| bool Context::interpret() { |
| Interpreter interpreter(mMemoryManager, mReplayRequest->getStackSize()); |
| registerCallbacks(&interpreter); |
| return interpreter.run(mReplayRequest->getInstructionList()) && mPostBuffer->flush(); |
| } |
| |
| void Context::registerCallbacks(Interpreter* interpreter) { |
| // Custom function for posting and fetching resources to and from the server |
| interpreter->registerBuiltin(Interpreter::POST_FUNCTION_ID, |
| [this](Stack* stack, bool) { return this->postData(stack); }); |
| interpreter->registerBuiltin(Interpreter::RESOURCE_FUNCTION_ID, |
| [this](Stack* stack, bool) { return this->loadResource(stack); }); |
| |
| // Registering custom synthetic functions |
| interpreter->registerBuiltin(Builtins::StartTimer, |
| [this](Stack* stack, bool) { return this->startTimer(stack); }); |
| interpreter->registerBuiltin(Builtins::StopTimer, |
| [this](Stack* stack, bool pushReturn) { |
| return this->stopTimer(stack, pushReturn); |
| }); |
| interpreter->registerBuiltin(Builtins::FlushPostBuffer, [this](Stack* stack, bool) { |
| return this->flushPostBuffer(stack); |
| }); |
| |
| interpreter->registerBuiltin(Builtins::ReplayCreateRenderer, [this](Stack* stack, bool) { |
| uint32_t id = stack->pop<uint32_t>(); |
| if (stack->isValid()) { |
| GAPID_INFO("replayCreateRenderer(%u)", id); |
| if (Renderer* prev = mRenderers[id]) { |
| if (mBoundRenderer == prev) { |
| mBoundRenderer = nullptr; |
| } |
| delete prev; |
| } |
| mRenderers[id] = Renderer::create(); |
| return true; |
| } else { |
| GAPID_WARNING("Error during calling function replayCreateRenderer"); |
| return false; |
| } |
| }); |
| |
| interpreter->registerBuiltin(Builtins::ReplayBindRenderer, [this, interpreter](Stack* stack, bool) { |
| uint32_t id = stack->pop<uint32_t>(); |
| if (stack->isValid()) { |
| if (mBoundRenderer != nullptr) { |
| mBoundRenderer->unbind(); |
| mBoundRenderer = nullptr; |
| } |
| mBoundRenderer = mRenderers[id]; |
| mBoundRenderer->bind(); |
| Api* api = mBoundRenderer->api(); |
| interpreter->setRendererFunctions(&api->mFunctions); |
| GAPID_INFO("Bound renderer %u: %s - %s", id, mBoundRenderer->name(), mBoundRenderer->version()); |
| return true; |
| } else { |
| GAPID_WARNING("Error during calling function replayBindRenderer"); |
| return false; |
| } |
| }); |
| |
| interpreter->registerBuiltin(Builtins::ContextInfo, [this](Stack* stack, bool) { |
| Renderer::Backbuffer backbuffer; |
| |
| bool preserveBuffersOnSwap = stack->pop<bool>(); |
| bool resetViewportScissor = stack->pop<bool>(); |
| backbuffer.format.stencil = stack->pop<uint32_t>(); |
| backbuffer.format.depth = stack->pop<uint32_t>(); |
| backbuffer.format.color = stack->pop<uint32_t>(); |
| backbuffer.height = stack->pop<int32_t>(); |
| backbuffer.width = stack->pop<int32_t>(); |
| const uint8_t* constant_data = stack->pop<uint8_t*>(); |
| const uint32_t* constant_sizes = stack->pop<uint32_t*>(); |
| const uint32_t* constant_offsets = stack->pop<uint32_t*>(); |
| const uint32_t* constant_names = stack->pop<uint32_t*>(); |
| const uint32_t constant_count = stack->pop<uint32_t>(); |
| |
| if (!stack->isValid()) { |
| GAPID_WARNING("Error during calling function replayCreateRenderer"); |
| return false; |
| } |
| |
| if (stack->isValid()) { |
| GAPID_INFO("contextInfo(%d, %d, 0x%x, 0x%x, 0x%x)", |
| backbuffer.width, |
| backbuffer.height, |
| backbuffer.format.color, |
| backbuffer.format.depth, |
| backbuffer.format.stencil, |
| resetViewportScissor ? "true" : "false"); |
| if (mBoundRenderer == nullptr) { |
| GAPID_INFO("contextInfo called without a bound renderer"); |
| return false; |
| } |
| mBoundRenderer->setBackbuffer(backbuffer); |
| auto gles = mBoundRenderer->getApi<Gles>(); |
| // TODO: This needs to change when we support other APIs. |
| GAPID_ASSERT(gles != nullptr); |
| if (resetViewportScissor) { |
| gles->glViewport(0, 0, backbuffer.width, backbuffer.height); |
| gles->glScissor(0, 0, backbuffer.width, backbuffer.height); |
| } |
| return true; |
| } else { |
| GAPID_WARNING("Error during calling function replayCreateRenderer"); |
| return false; |
| } |
| }); |
| } |
| |
| bool Context::loadResource(Stack* stack) { |
| uint32_t resourceId = stack->pop<uint32_t>(); |
| void* address = stack->pop<void*>(); |
| |
| if (!stack->isValid()) { |
| GAPID_WARNING("Error during loadResource"); |
| return false; |
| } |
| |
| const auto& resource = mReplayRequest->getResources()[resourceId]; |
| |
| if (!mResourceProvider->get(&resource, 1, mServer, address, resource.size)) { |
| GAPID_WARNING("Can't fetch resource: %s", resource.id.c_str()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool Context::postData(Stack* stack) { |
| const uint32_t count = stack->pop<uint32_t>(); |
| const void* address = stack->pop<const void*>(); |
| |
| if (!stack->isValid()) { |
| GAPID_WARNING("Error during postData"); |
| return false; |
| } |
| |
| return mPostBuffer->push(address, count); |
| } |
| |
| bool Context::flushPostBuffer(Stack* stack) { |
| if (!stack->isValid()) { |
| GAPID_WARNING("Error during flushPostBuffer"); |
| return false; |
| } |
| |
| return mPostBuffer->flush(); |
| } |
| |
| bool Context::startTimer(Stack* stack) { |
| size_t index = static_cast<size_t>(stack->pop<uint8_t>()); |
| if (stack->isValid()) { |
| if (index < MAX_TIMERS) { |
| GAPID_INFO("startTimer(%d)", index); |
| mTimers[index].Start(); |
| return true; |
| } else { |
| GAPID_WARNING("StartTimer called with invalid index %d", index); |
| } |
| } else { |
| GAPID_WARNING("Error while calling function StartTimer"); |
| } |
| return false; |
| } |
| |
| bool Context::stopTimer(Stack* stack, bool pushReturn) { |
| size_t index = static_cast<size_t>(stack->pop<uint8_t>()); |
| if (stack->isValid()) { |
| if (index < MAX_TIMERS) { |
| GAPID_INFO("stopTimer(%d)", index); |
| uint64_t ns = mTimers[index].Stop(); |
| if (pushReturn) { |
| stack->push(ns); |
| } |
| return true; |
| } else { |
| GAPID_WARNING("StopTimer called with invalid index %d", index); |
| } |
| } else { |
| GAPID_WARNING("Error while calling function StopTimer"); |
| } |
| return false; |
| } |
| |
| } // namespace gapir |