// // Copyright 2025 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // FrameCaptureCL.cpp: // ANGLE CL Frame capture implementation. // #include "libANGLE/capture/FrameCapture.h" #include "common/angle_version_info.h" #include "common/frame_capture_utils.h" #include "common/serializer/JsonSerializer.h" #include "common/string_utils.h" #include "common/system_utils.h" #include "libANGLE/CLBuffer.h" #include "libANGLE/CLCommandQueue.h" #include "libANGLE/CLContext.h" #include "libANGLE/CLImage.h" #include "libANGLE/CLProgram.h" #include "libANGLE/capture/capture_cl_autogen.h" #include "libANGLE/capture/serialize.h" #include "libANGLE/cl_utils.h" #include "libGLESv2/cl_stubs_autogen.h" #if !ANGLE_CAPTURE_ENABLED # error Frame capture must be enabled to include this file. #endif // !ANGLE_CAPTURE_ENABLED #ifndef ANGLE_ENABLE_CL # error OpenCL must be enabled to include this file. #endif // !ANGLE_ENABLE_CL namespace angle { // Some replay functions can get quite large. If over a certain size, this method breaks up the // function into parts to avoid overflowing the stack and causing slow compilation. void WriteCppReplayFunctionWithPartsCL(ReplayFunc replayFunc, ReplayWriter &replayWriter, uint32_t frameIndex, std::vector *binaryData, const std::vector &calls, std::stringstream &header, std::stringstream &out) { out << "void " << FmtFunction(replayFunc, kNoContextId, FuncUsage::Definition, frameIndex, kNoPartId) << "\n" << "{\n"; for (const CallCapture &call : calls) { // Process active calls for Setup and inactive calls for SetupInactive if ((call.isActive && replayFunc != ReplayFunc::SetupInactive) || (!call.isActive && replayFunc == ReplayFunc::SetupInactive)) { out << " "; WriteCppReplayForCallCL(call, replayWriter, out, header, binaryData); out << ";\n"; } } out << "}\n"; } void WriteCppReplayForCallCL(const CallCapture &call, ReplayWriter &replayWriter, std::ostream &out, std::ostream &header, std::vector *binaryData) { if (call.customFunctionName == "Comment") { // Just write it directly to the file and move on WriteComment(out, call); return; } std::ostringstream callOut; std::ostringstream postCallAdditions; const ParamCapture &returnValue = call.params.getReturnValue(); switch (returnValue.type) { case ParamType::Tcl_context: callOut << "clContextsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &returnValue.value.cl_contextVal) << "] = "; break; case ParamType::Tcl_command_queue: callOut << "clCommandQueuesMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &returnValue.value.cl_command_queueVal) << "] = "; break; case ParamType::Tcl_mem: callOut << "clMemMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &returnValue.value.cl_memVal) << "] = "; break; case ParamType::Tcl_sampler: callOut << "clSamplerMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &returnValue.value.cl_samplerVal) << "] = "; break; case ParamType::Tcl_program: callOut << "clProgramsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &returnValue.value.cl_programVal) << "] = "; break; case ParamType::Tcl_kernel: callOut << "clKernelsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &returnValue.value.cl_kernelVal) << "] = "; break; case ParamType::Tcl_event: callOut << "clEventsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &returnValue.value.cl_eventVal) << "] = "; break; case ParamType::TvoidPointer: if (cl::Platform::GetDefault()->getFrameCaptureShared()->getCLVoidIndex( returnValue.value.voidPointerVal) != SIZE_MAX) { callOut << "clVoidMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getCLVoidIndex( returnValue.value.voidPointerVal) << "] = "; } break; default: break; } callOut << call.name() << "("; bool first = true; for (const ParamCapture ¶m : call.params.getParamCaptures()) { if (!first) { callOut << ", "; } if (param.arrayClientPointerIndex != -1 && param.value.voidConstPointerVal != nullptr) { callOut << "gClientArrays[" << param.arrayClientPointerIndex << "]"; } else if (param.readBufferSizeBytes > 0) { callOut << "(" << ParamTypeToString(param.type) << ")gReadBuffer"; } else if (param.data.empty()) { if (param.type == ParamType::Tcl_platform_idPointer && param.value.cl_platform_idPointerVal) { callOut << "clPlatformsMap"; } else if (param.type == ParamType::Tcl_platform_id) { callOut << "clPlatformsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_platform_idVal) << "]"; } else if (param.type == ParamType::Tcl_device_idPointer && param.value.cl_device_idPointerVal) { std::vector tempDeviceIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); cl_uint numDevices = call.params.getParamCaptures()[2].value.cl_uintVal; out << "temporaryDevicesList.clear();\n temporaryDevicesList.resize(" << numDevices << ");\n "; callOut << "temporaryDevicesList.data()"; for (cl_uint i = 0; i < numDevices; ++i) { postCallAdditions << ";\n clDevicesMap[" << tempDeviceIndices[i] << "] = temporaryDevicesList[" << i << "]"; } } else if (param.type == ParamType::Tcl_device_id) { callOut << "clDevicesMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_device_idVal) << "]"; } else if (param.type == ParamType::Tcl_context) { callOut << "clContextsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_contextVal) << "]"; } else if (param.type == ParamType::Tcl_command_queue) { callOut << "clCommandQueuesMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_command_queueVal) << "]"; } else if (param.type == ParamType::Tcl_mem) { callOut << "clMemMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_memVal) << "]"; } else if (param.type == ParamType::Tcl_sampler) { callOut << "clSamplerMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_samplerVal) << "]"; } else if (param.type == ParamType::Tcl_program) { callOut << "clProgramsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_programVal) << "]"; } else if (param.type == ParamType::Tcl_kernel) { callOut << "clKernelsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_kernelVal) << "]"; } else if (param.type == ParamType::Tcl_event) { callOut << "clEventsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_eventVal) << "]"; } else if (param.type == ParamType::Tcl_eventPointer) { if (param.value.cl_eventPointerVal) { callOut << "&clEventsMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_eventVal) << "]"; } else { callOut << "NULL"; } } else if (param.type == ParamType::TvoidConstPointer) { if (cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_memVal) != SIZE_MAX) { callOut << "(const void *)" << "&clMemMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_memVal) << "]"; } else if (cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_samplerVal) != SIZE_MAX) { callOut << "(const void *)" << "&clSamplerMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_samplerVal) << "]"; } else if (cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_command_queueVal) != SIZE_MAX) { callOut << "(const void *)" << "&clCommandQueuesMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( ¶m.value.cl_command_queueVal) << "]"; } else if (cl::Platform::GetDefault()->getFrameCaptureShared()->getCLVoidIndex( param.value.voidConstPointerVal) != SIZE_MAX) { callOut << "clVoidMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getCLVoidIndex( param.value.voidConstPointerVal) << "]"; } else { WriteParamCaptureReplay(callOut, call, param); } } else if (param.type == ParamType::TvoidPointer) { if (cl::Platform::GetDefault()->getFrameCaptureShared()->getCLVoidIndex( param.value.voidPointerVal) != SIZE_MAX) { callOut << "clVoidMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getCLVoidIndex( param.value.voidPointerVal) << "]"; } else { WriteParamCaptureReplay(callOut, call, param); } } else if (param.type == ParamType::Tcl_mem_destructor_func_type || param.type == ParamType::Tcl_callback_func_type || param.type == ParamType::Tcl_svm_free_callback_func_type || param.type == ParamType::Tcl_program_func_type || param.type == ParamType::Tcl_context_destructor_func_type || param.type == ParamType::Tcl_context_func_type || param.type == ParamType::Tcl_void_func_type) { callOut << "NULL"; } else if (param.type == ParamType::Tcl_memConstPointer && cl::Platform::GetDefault() ->getFrameCaptureShared() ->getCLObjVector(¶m) .size()) { std::vector tempBufferIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); out << "temporaryBuffersList = {"; for (uint32_t i = 0; i < tempBufferIndices.size(); ++i) { out << (i != 0 ? ", " : "") << "clMemMap[" << tempBufferIndices.at(i) << "]"; } out << "};\n "; callOut << "temporaryBuffersList.data()"; } else if (param.type == ParamType::Tcl_eventConstPointer) { std::vector tempEventIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); if (tempEventIndices.empty()) { callOut << "NULL"; } else { out << "temporaryEventsList = {"; for (uint32_t i = 0; i < tempEventIndices.size(); ++i) { out << (i != 0 ? ", " : "") << "clEventsMap[" << tempEventIndices.at(i) << "]"; } out << "};\n "; callOut << "temporaryEventsList.data()"; } } else if (param.type == ParamType::Tcl_device_idConstPointer) { std::vector tempDeviceIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); if (tempDeviceIndices.empty()) { callOut << "NULL"; } else { out << "temporaryDevicesList = {"; for (uint32_t i = 0; i < tempDeviceIndices.size(); ++i) { if (i != 0) { out << ", "; } out << "clDevicesMap[" << tempDeviceIndices.at(i) << "]"; } out << "};\n "; callOut << "temporaryDevicesList.data()"; } } else if (param.type == ParamType::Tcl_kernelPointer) { std::vector tempKernelIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); cl_uint numKernels = call.params.getParamCaptures()[1].value.cl_uintVal; out << "temporaryKernelsList.clear();\ntemporaryKernelsList.resize(" << numKernels << ");\n "; callOut << "temporaryKernelsList.data()"; for (cl_uint i = 0; i < numKernels; ++i) { postCallAdditions << ";\n clKernelsMap[" << tempKernelIndices[i] << "] = temporaryKernelsList[" << i << "]"; } } else if (param.type == ParamType::TvoidConstPointerPointer && cl::Platform::GetDefault() ->getFrameCaptureShared() ->getCLObjVector(¶m) .size()) { std::vector offsets = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); out << "temporaryVoidPtrList = {"; for (size_t i = 0; i < offsets.size(); ++i) { out << (i != 0 ? ", " : "") << "&((char*)temporaryVoidPtr)[" << offsets.at(i) << "]"; } out << "};\n "; callOut << "temporaryVoidPtrList.data()"; } else if (param.type == ParamType::TvoidPointerPointer || param.type == ParamType::TvoidConstPointerPointer) { std::vector tempVoidIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); out << "temporaryVoidPtrList = {"; for (uint32_t i = 0; i < tempVoidIndices.size(); ++i) { out << (i != 0 ? ", " : "") << "clVoidMap[" << tempVoidIndices.at(i) << "]"; } out << "};\n "; callOut << "temporaryVoidPtrList.data()"; } else if (param.type == ParamType::Tcl_programConstPointer && param.value.size_tVal) { std::vector tempProgramIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector(¶m); out << "temporaryProgramsList = {"; for (uint32_t i = 0; i < tempProgramIndices.size(); ++i) { out << (i != 0 ? ", " : "") << "clProgramsMap[" << tempProgramIndices.at(i) << "]"; } out << "};\n "; callOut << "temporaryProgramsList.data()"; } else if (param.type == ParamType::Tcl_context_propertiesConstPointer) { if (param.value.cl_context_propertiesConstPointerVal) { callOut << "temporaryContextProps.data()"; } else { WriteParamCaptureReplay(callOut, call, param); } } else { WriteParamCaptureReplay(callOut, call, param); } } else { switch (param.type) { case ParamType::TcharConstPointerPointer: WriteStringPointerParamReplay(replayWriter, callOut, header, call, param); break; case ParamType::Tcl_device_idPointer: callOut << "clDevicesMap"; break; case ParamType::TcharUnsignedConstPointerPointer: { std::string tempStructureName = "temporaryCharPointerList"; std::string tempStructureType = "(const char *)"; if (param.type == ParamType::TcharUnsignedConstPointerPointer) { tempStructureName = "temporaryUnsignedCharPointerList"; tempStructureType = "(const unsigned char *)"; } const std::vector *data; out << tempStructureName << " = {"; for (size_t i = 0; i < param.data.size(); ++i) { if (i != 0) { out << ", "; } data = ¶m.data[i]; size_t offset = rx::roundUpPow2(binaryData->size(), kBinaryAlignment); binaryData->resize(offset + data->size()); memcpy(binaryData->data() + offset, data->data(), data->size()); out << tempStructureType << "&gBinaryData[" << offset << "]"; } out << "};\n "; callOut << tempStructureName << ".data()"; break; } case ParamType::Tcl_image_descConstPointer: cl_image_desc tempImageDesc; std::memcpy(&tempImageDesc, param.data[0].data(), sizeof(cl_image_desc)); if (tempImageDesc.mem_object) { out << " std::memcpy(&temporaryImageDesc, "; WriteBinaryParamReplay(replayWriter, out, header, call, param, binaryData); out << ", sizeof(cl_image_desc));\ntemporaryImageDesc.mem_object = " "clMemMap[" << cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex( &tempImageDesc.mem_object) << "];\n "; callOut << "&temporaryImageDesc"; } else { WriteBinaryParamReplay(replayWriter, callOut, header, call, param, binaryData); } break; case ParamType::TvoidPointer: { // For clEnqueueNativeKernel if (call.entryPoint == EntryPoint::CLEnqueueNativeKernel) { std::vector bufferIndices = cl::Platform::GetDefault()->getFrameCaptureShared()->getCLObjVector( ¶m); size_t totalSize = call.params.getParamCaptures()[3].value.size_tVal; out << "temporaryVoidPtr = (void *)std::malloc(" << totalSize << ");\nstd::memcpy(&temporaryVoidPtr, "; WriteBinaryParamReplay(replayWriter, out, header, call, param, binaryData); out << ", " << totalSize << ");\n "; callOut << "temporaryVoidPtr"; } else { WriteBinaryParamReplay(replayWriter, callOut, header, call, param, binaryData); } break; } default: WriteBinaryParamReplay(replayWriter, callOut, header, call, param, binaryData); break; } } first = false; } callOut << ")"; out << callOut.str() << postCallAdditions.str(); } void WriteInitReplayCallCL(bool compression, std::ostream &out, const std::string &captureLabel, size_t maxClientArraySize, size_t readBufferSize, const std::map &maxCLParamsSize) { std::string binaryDataFileName = GetBinaryDataFilePath(compression, captureLabel); out << " // binaryDataFileName = " << binaryDataFileName << "\n"; out << " // maxClientArraySize = " << maxClientArraySize << "\n"; out << " // readBufferSize = " << readBufferSize << "\n"; out << " // clPlatformsMapSize = " << maxCLParamsSize.at(ParamType::Tcl_platform_idPointer) << "\n"; out << " // clDevicesMapSize = " << maxCLParamsSize.at(ParamType::Tcl_device_idPointer) << "\n"; out << " // clContextsMapSize = " << maxCLParamsSize.at(ParamType::Tcl_context) << "\n"; out << " // clCommandQueuesMapSize = " << maxCLParamsSize.at(ParamType::Tcl_command_queue) << "\n"; out << " // clMemMapSize = " << maxCLParamsSize.at(ParamType::Tcl_mem) << "\n"; out << " // clEventsMapSize = " << maxCLParamsSize.at(ParamType::Tcl_eventPointer) << "\n"; out << " // clProgramsMapSize = " << maxCLParamsSize.at(ParamType::Tcl_program) << "\n"; out << " // clKernelsMapSize = " << maxCLParamsSize.at(ParamType::Tcl_kernel) << "\n"; out << " // clSamplerMapSize = " << maxCLParamsSize.at(ParamType::Tcl_sampler) << "\n"; out << " // clVoidMapSize = " << maxCLParamsSize.at(ParamType::TvoidPointer) << "\n"; out << " InitializeReplayCL(\"" << binaryDataFileName << "\", " << maxClientArraySize << ", " << readBufferSize << ", " << maxCLParamsSize.at(ParamType::Tcl_platform_idPointer) << ", " << maxCLParamsSize.at(ParamType::Tcl_device_idPointer) << ", " << maxCLParamsSize.at(ParamType::Tcl_context) << ", " << maxCLParamsSize.at(ParamType::Tcl_command_queue) << ", " << maxCLParamsSize.at(ParamType::Tcl_mem) << ", " << maxCLParamsSize.at(ParamType::Tcl_eventPointer) << ", " << maxCLParamsSize.at(ParamType::Tcl_program) << ", " << maxCLParamsSize.at(ParamType::Tcl_kernel) << ", " << maxCLParamsSize.at(ParamType::Tcl_sampler) << ", " << maxCLParamsSize.at(ParamType::TvoidPointer) << ");\n"; } void FrameCaptureShared::trackCLMemUpdate(const cl_mem *mem, bool referenced) { std::vector &mCLDirtyMem = mResourceTrackerCL.mCLDirtyMem; // retained or created cl mem object if (referenced) { // Potentially mark as dirty auto it = std::find(mCLDirtyMem.begin(), mCLDirtyMem.end(), *mem); if (it == mCLDirtyMem.end()) { mCLDirtyMem.push_back(*mem); } } else { std::unordered_map &mCLSubBufferToParent = mResourceTrackerCL.mCLSubBufferToParent; if ((*mem)->cast().getRefCount() == 1) { auto it = std::find(mCLDirtyMem.begin(), mCLDirtyMem.end(), *mem); if (it != mCLDirtyMem.end()) { mCLDirtyMem.erase(it); } if (removeUnneededOpenCLCalls) { removeCLMemOccurrences(mem, &mFrameCalls); } mCLSubBufferToParent.erase(*mem); (*mem)->cast().release(); } if (mCLSubBufferToParent.find(*mem) != mCLSubBufferToParent.end()) { trackCLMemUpdate(&mCLSubBufferToParent[*mem], false); } } } void FrameCaptureShared::trackCLProgramUpdate(const cl_program *program, bool referenced, cl_uint numLinkedPrograms, const cl_program *linkedPrograms) { std::unordered_map &mCLProgramLinkCounter = mResourceTrackerCL.mCLProgramLinkCounter; std::unordered_map> &mCLLinkedPrograms = mResourceTrackerCL.mCLLinkedPrograms; // retained or created cl program object if (referenced) { // Increment link count for this program if (mCLProgramLinkCounter.find(*program) == mCLProgramLinkCounter.end()) { mCLProgramLinkCounter[*program] = 0; } ++mCLProgramLinkCounter[*program]; // Setup the linked programs if this call is from capturing clCompileProgram or // clLinkProgram if (numLinkedPrograms) { mCLLinkedPrograms[*program] = std::vector(); for (cl_uint i = 0; i < numLinkedPrograms; ++i) { mCLLinkedPrograms[*program].push_back(linkedPrograms[i]); } } // Go through the linked programs and increment their link counts for (size_t i = 0; mCLLinkedPrograms.find(*program) != mCLLinkedPrograms.end() && i < mCLLinkedPrograms[*program].size(); ++i) { trackCLProgramUpdate(&mCLLinkedPrograms[*program].at(i), true, 0, nullptr); } } else { // Decrement link count for this program and the linked programs --mCLProgramLinkCounter[*program]; for (size_t i = 0; mCLLinkedPrograms.find(*program) != mCLLinkedPrograms.end() && i < mCLLinkedPrograms[*program].size(); ++i) { trackCLProgramUpdate(&mCLLinkedPrograms[*program].at(i), false, 0, nullptr); } // Remove the calls containing this object if the link count is 0 if (mCLProgramLinkCounter[*program] == 0) { mCLProgramLinkCounter.erase(*program); if (mCLLinkedPrograms.find(*program) != mCLLinkedPrograms.end()) { mCLLinkedPrograms.erase(*program); } if (removeUnneededOpenCLCalls) { removeCLProgramOccurrences(program, &mFrameCalls); } } } } void FrameCaptureShared::injectMemcpy(void *src, void *dest, size_t size, std::vector *calls) { // Inject memcpy call before unmap // Create param buffer ParamBuffer paramBuffer; // Create dest parameter ParamCapture destParam("dest", ParamType::TvoidConstPointer); InitParamValue(ParamType::TvoidPointer, dest, &destParam.value); paramBuffer.addParam(std::move(destParam)); // Create src param ParamCapture updateMemory("src", ParamType::TvoidConstPointer); CaptureMemory(src, size, &updateMemory); paramBuffer.addParam(std::move(updateMemory)); paramBuffer.addValueParam("size", ParamType::Tsize_t, size); calls->emplace(calls->end() - 1, "std::memcpy", std::move(paramBuffer)); } void FrameCaptureShared::captureUpdateCLObjs(std::vector *calls) { std::vector &mCLDirtyMem = mResourceTrackerCL.mCLDirtyMem; std::vector &mCLDirtySVM = mResourceTrackerCL.mCLDirtySVM; cl_command_queue &mCLCurrentCommandQueue = mResourceTrackerCL.mCLCurrentCommandQueue; for (uint32_t i = 0; i < mCLDirtyMem.size(); ++i) { cl_mem_object_type memType; if (IsError(mCLDirtyMem.at(i)->cast().getInfo( cl::MemInfo::Type, sizeof(cl_mem_object_type), &memType, nullptr))) { continue; } if (memType == CL_MEM_OBJECT_BUFFER) { void *ptr; if (calls->back().entryPoint == EntryPoint::CLEnqueueUnmapMemObject) { CallCapture *mapCall = &mResourceTrackerCL.mCLMapCall.at( calls->back() .params.getParam("mapped_ptr", ParamType::TvoidPointer, 2) .value.voidPointerVal); size_t offset = mapCall->params.getParam("offset", ParamType::Tsize_t, 4).value.size_tVal; size_t size = mapCall->params.getParam("size", ParamType::Tsize_t, 5).value.size_tVal; ptr = malloc(size); // Call clEnqueueReadBuffer to get the current data in the buffer EnqueueReadBuffer(mCLCurrentCommandQueue, mCLDirtyMem.at(i), true, offset, size, ptr, 0, nullptr, nullptr); // Inject memcpy call BEFORE unmap injectMemcpy(ptr, calls->back() .params.getParam("mapped_ptr", ParamType::TvoidPointer, 2) .value.voidPointerVal, size, calls); } else { size_t bufferSize = mCLDirtyMem.at(i)->cast().getSize(); ptr = malloc(bufferSize); // Call clEnqueueReadBuffer to get the current data in the buffer EnqueueReadBuffer(mCLCurrentCommandQueue, mCLDirtyMem.at(i), true, 0, bufferSize, ptr, 0, nullptr, nullptr); // Pretend that a "clEnqueueWriteBuffer" was called with the above data retrieved calls->push_back(CaptureEnqueueWriteBuffer(true, mCLCurrentCommandQueue, mCLDirtyMem.at(i), true, 0, bufferSize, ptr, 0, nullptr, nullptr, CL_SUCCESS)); // Implicit release, so going into the starting frame the buffer has the correct // reference count mCLDirtyMem.at(i)->cast().release(); } free(ptr); } else if (memType == CL_MEM_OBJECT_PIPE) { UNIMPLEMENTED(); } else { cl::Image *clImg = &mCLDirtyMem.at(i)->cast(); void *ptr; if (calls->back().entryPoint == EntryPoint::CLEnqueueUnmapMemObject) { CallCapture *mapCall = &mResourceTrackerCL.mCLMapCall.at( calls->back() .params.getParam("mapped_ptr", ParamType::TvoidPointer, 2) .value.voidPointerVal); const size_t *origin = (const size_t *)mapCall->params .getParam("origin", ParamType::Tsize_tConstPointer, 4) .data.back() .data(); const size_t *region = (const size_t *)mapCall->params .getParam("region", ParamType::Tsize_tConstPointer, 5) .data.back() .data(); size_t rowPitch = mapCall->params.getParam("image_row_pitch", ParamType::Tsize_t, 6) .value.size_tVal; size_t slicePitch = mapCall->params.getParam("image_slice_pitch", ParamType::Tsize_t, 7) .value.size_tVal; // Get the image size to allocate the size of ptr size_t totalSize = (region[2] - 1) * slicePitch + (region[1] - 1) * rowPitch + region[0] * clImg->getElementSize(); ptr = malloc(totalSize); // Call clEnqueueReadBuffer to get the current data in the image EnqueueReadImage(mCLCurrentCommandQueue, mCLDirtyMem.at(i), true, origin, region, rowPitch, slicePitch, ptr, 0, nullptr, nullptr); // Inject memcpy call BEFORE unmap injectMemcpy(ptr, calls->back() .params.getParam("mapped_ptr", ParamType::TvoidPointer, 2) .value.voidPointerVal, totalSize, calls); } else { ptr = malloc(clImg->getSize()); size_t origin[3] = {0, 0, 0}; size_t region[3] = {clImg->getWidth(), clImg->getHeight(), clImg->getDepth()}; // Call clEnqueueReadBuffer to get the current data in the image EnqueueReadImage(mCLCurrentCommandQueue, mCLDirtyMem.at(i), true, origin, region, clImg->getRowSize(), clImg->getSliceSize(), ptr, 0, nullptr, nullptr); // Pretend that a "clEnqueueWriteImage" was called with the above data retrieved calls->push_back(CaptureEnqueueWriteImage( true, mCLCurrentCommandQueue, mCLDirtyMem.at(i), true, origin, region, clImg->getRowSize(), clImg->getSliceSize(), ptr, 0, nullptr, nullptr, CL_SUCCESS)); // Implicit release, so going into the starting frame the buffer has the correct // reference count mCLDirtyMem.at(i)->cast().release(); } free(ptr); } } for (uint32_t i = 0; i < mCLDirtySVM.size(); ++i) { size_t SVMSize = mResourceTrackerCL.SVMToSize[mCLDirtySVM.at(i)]; // Call clEnqueueSVMMap to get the current data in the SVM pointer cl::MemFlags flags; flags.set(CL_MAP_READ); EnqueueSVMMap(mCLCurrentCommandQueue, true, flags, mCLDirtySVM.at(i), SVMSize, 0, nullptr, nullptr); // Pretend that a "clEnqueueSVMMemcpy" was called with the above data retrieved calls->push_back(CaptureEnqueueSVMMemcpy(true, mCLCurrentCommandQueue, true, mCLDirtySVM.at(i), mCLDirtySVM.at(i), SVMSize, 0, nullptr, nullptr, CL_SUCCESS)); // Call clEnqueueSVMUnmap to get the current data in the SVM pointer EnqueueSVMUnmap(mCLCurrentCommandQueue, mResourceTrackerCL.mCLDirtySVM.at(i), 0, nullptr, nullptr); } mCLDirtyMem.clear(); mCLDirtySVM.clear(); } void FrameCaptureShared::removeCLMemOccurrences(const cl_mem *mem, std::vector *calls) { // This function gets called when it captures a clReleaseMemObj prior to the starting frame // that sets the reference count to 0, meaning that this cl_mem object isn't necessary for // the wanted frames. So, we can remove the calls that use it. for (size_t i = 0; i < calls->size(); ++i) { CallCapture *call = &calls->at(i); cl_mem foundMem; switch (call->entryPoint) { case EntryPoint::CLCreateBuffer: case EntryPoint::CLCreateBufferWithProperties: case EntryPoint::CLCreateImage: case EntryPoint::CLCreateImageWithProperties: case EntryPoint::CLCreateImage2D: case EntryPoint::CLCreateImage3D: case EntryPoint::CLCreatePipe: { foundMem = call->params.getReturnValue().value.cl_memVal; break; } case EntryPoint::CLCreateSubBuffer: { foundMem = call->params.getReturnValue().value.cl_memVal; if (foundMem != *mem) { foundMem = call->params.getParam("buffer", ParamType::Tcl_mem, 0).value.cl_memVal; } break; } case EntryPoint::CLEnqueueReadBuffer: case EntryPoint::CLEnqueueWriteBuffer: case EntryPoint::CLEnqueueReadBufferRect: case EntryPoint::CLEnqueueWriteBufferRect: case EntryPoint::CLEnqueueMapBuffer: { // Can get rid of these calls because the buffer is no longer needed foundMem = call->params.getParam("buffer", ParamType::Tcl_mem, 1).value.cl_memVal; break; } case EntryPoint::CLEnqueueReadImage: case EntryPoint::CLEnqueueWriteImage: case EntryPoint::CLEnqueueMapImage: { // Can get rid of these calls because the image is no longer needed foundMem = call->params.getParam("image", ParamType::Tcl_mem, 1).value.cl_memVal; break; } case EntryPoint::CLEnqueueCopyBuffer: case EntryPoint::CLEnqueueCopyBufferRect: case EntryPoint::CLEnqueueCopyImage: case EntryPoint::CLEnqueueCopyBufferToImage: case EntryPoint::CLEnqueueCopyImageToBuffer: { // Can get rid of these calls because the obj is no longer needed std::string srcType = "src_"; srcType += ((call->entryPoint == EntryPoint::CLEnqueueCopyImage || call->entryPoint == EntryPoint::CLEnqueueCopyImageToBuffer) ? "image" : "buffer"); std::string dstType = "dst_"; dstType += ((call->entryPoint == EntryPoint::CLEnqueueCopyImage || call->entryPoint == EntryPoint::CLEnqueueCopyBufferToImage) ? "image" : "buffer"); foundMem = call->params.getParam(srcType.c_str(), ParamType::Tcl_mem, 1).value.cl_memVal; if (foundMem != *mem) { foundMem = call->params.getParam(dstType.c_str(), ParamType::Tcl_mem, 2) .value.cl_memVal; } break; } case EntryPoint::CLReleaseMemObject: case EntryPoint::CLRetainMemObject: case EntryPoint::CLGetMemObjectInfo: case EntryPoint::CLSetMemObjectDestructorCallback: case EntryPoint::CLEnqueueUnmapMemObject: { foundMem = call->params .getParam("memobj", ParamType::Tcl_mem, call->entryPoint == EntryPoint::CLEnqueueUnmapMemObject ? 1 : 0) .value.cl_memVal; break; } case EntryPoint::CLGetImageInfo: { foundMem = call->params.getParam("image", ParamType::Tcl_mem, 0).value.cl_memVal; break; } case EntryPoint::CLSetKernelArg: { foundMem = call->params.getParam("arg_value", ParamType::TvoidConstPointer, 3) .value.cl_memVal; if (cl::Platform::GetDefault()->getFrameCaptureShared()->getIndex(&foundMem) == SIZE_MAX) { continue; } break; } // Leave commented until external memory is upstream // case EntryPoint::CLEnqueueAcquireExternalMemObjectsKHR: // case EntryPoint::CLEnqueueReleaseExternalMemObjectsKHR: case EntryPoint::CLEnqueueMigrateMemObjects: { const cl_mem *memObjs = call->params.getParam("mem_objects", ParamType::Tcl_memConstPointer, 2) .value.cl_memConstPointerVal; cl_uint numMemObjs = call->params.getParam("num_mem_objects", ParamType::Tcl_uint, 1) .value.cl_uintVal; std::vector newMemObjs; for (cl_uint memObjIndex = 0; i < numMemObjs; ++i) { if (memObjs[memObjIndex] != *mem) { newMemObjs.push_back(memObjs[memObjIndex]); } } // If all the mem objects used in this array are released, I can remove this call if (newMemObjs.empty()) { foundMem = *mem; } else { call->params.setValueParamAtIndex("num_mem_objects", ParamType::Tcl_uint, newMemObjs.size(), 1); setCLObjVectorMap( newMemObjs.data(), newMemObjs.size(), &call->params.getParam("mem_objects", ParamType::Tcl_memConstPointer, 2), &angle::FrameCaptureShared::getIndex); continue; } break; } default: continue; } if (foundMem == *mem) { removeCLCall(calls, i); --i; } } } void FrameCaptureShared::removeCLKernelOccurrences(const cl_kernel *kernel, std::vector *calls) { // This function gets called when it captures a clReleaseProgram prior to the starting frame // that sets the program's reference count to 0. This ensures that the kernels in that program // are/should be released as well, meaning that this cl_kernel object isn't necessary for // the wanted frames. So, we can remove the calls that use it. // We cannot remove cl_kernel occurrences at the time of clReleaseKernel because the kernel may // be an input to clCloneKernel and clCreateKernelsInProgram. for (size_t i = 0; i < calls->size(); ++i) { CallCapture *call = &calls->at(i); cl_kernel foundKernel; switch (call->entryPoint) { case EntryPoint::CLCreateKernel: { foundKernel = call->params.getReturnValue().value.cl_kernelVal; break; } case EntryPoint::CLCloneKernel: { foundKernel = call->params.getReturnValue().value.cl_kernelVal; if (foundKernel != *kernel) { foundKernel = call->params.getParam("source_kernel", ParamType::Tcl_kernel, 0) .value.cl_kernelVal; } break; } case EntryPoint::CLRetainKernel: case EntryPoint::CLReleaseKernel: case EntryPoint::CLSetKernelArg: case EntryPoint::CLSetKernelArgSVMPointer: case EntryPoint::CLSetKernelExecInfo: case EntryPoint::CLGetKernelInfo: case EntryPoint::CLGetKernelArgInfo: case EntryPoint::CLGetKernelWorkGroupInfo: case EntryPoint::CLGetKernelSubGroupInfo: { foundKernel = call->params.getParam("kernel", ParamType::Tcl_kernel, 0).value.cl_kernelVal; break; } case EntryPoint::CLEnqueueNDRangeKernel: case EntryPoint::CLEnqueueTask: { foundKernel = call->params.getParam("kernel", ParamType::Tcl_kernel, 1).value.cl_kernelVal; break; } default: continue; } if (foundKernel == *kernel) { removeCLCall(calls, i); --i; } } } void FrameCaptureShared::removeCLProgramOccurrences(const cl_program *program, std::vector *calls) { // This function gets called when it captures a clReleaseMemObj prior to the starting frame // that sets the reference count to 0, and the program is not linked to any other program, // meaning that this cl_mem object isn't necessary for the wanted frames. So, we can // remove the calls that use it. for (size_t i = 0; i < calls->size(); ++i) { CallCapture *call = &calls->at(i); cl_program foundProgram; switch (call->entryPoint) { case EntryPoint::CLCreateProgramWithSource: case EntryPoint::CLCreateProgramWithBinary: case EntryPoint::CLCreateProgramWithBuiltInKernels: case EntryPoint::CLCreateProgramWithIL: case EntryPoint::CLLinkProgram: { foundProgram = call->params.getReturnValue().value.cl_programVal; break; } case EntryPoint::CLRetainProgram: case EntryPoint::CLReleaseProgram: case EntryPoint::CLBuildProgram: case EntryPoint::CLGetProgramInfo: case EntryPoint::CLGetProgramBuildInfo: case EntryPoint::CLCreateKernel: case EntryPoint::CLCreateKernelsInProgram: case EntryPoint::CLUnloadPlatformCompiler: case EntryPoint::CLCompileProgram: { uint8_t programIndex = call->entryPoint == EntryPoint::CLCompileProgram ? 1 : 0; foundProgram = call->params.getParam("program", ParamType::Tcl_program, programIndex) .value.cl_programVal; break; } default: continue; } if (foundProgram == *program) { removeCLCall(calls, i); --i; } } if (mResourceTrackerCL.mCLProgramToKernels.find(*program) != mResourceTrackerCL.mCLProgramToKernels.end()) { for (size_t i = 0; i < mResourceTrackerCL.mCLProgramToKernels[*program].size(); ++i) { removeCLKernelOccurrences(&mResourceTrackerCL.mCLProgramToKernels[*program].at(i), calls); } mResourceTrackerCL.mCLProgramToKernels.erase(*program); } } void FrameCaptureShared::removeCLCall(std::vector *callVector, size_t &callIndex) { CallCapture *call = &callVector->at(callIndex); const std::vector *params = &call->params.getParamCaptures(); cl_context context = nullptr; // Checks if there is an event that is implicitly created during the deleted call. // If there is, need to inject a clCreateUserEvent call and a clSetUserEventStatus call. for (auto ¶m : *params) { if (param.type == ParamType::Tcl_context) { context = param.value.cl_contextVal; } else if (param.type == ParamType::Tcl_command_queue) { context = param.value.cl_command_queueVal->cast().getContext().getNative(); } else if (param.type == ParamType::Tcl_eventPointer && param.value.cl_eventVal != nullptr && context) { // Capture the creation of a successful event if the CL call being removed created an // event (ex: clEnqueueReadBuffer) cl_event event = param.value.cl_eventVal; callVector->insert(callVector->begin() + callIndex, CaptureSetUserEventStatus(true, event, CL_COMPLETE, CL_SUCCESS)); callVector->insert(callVector->begin() + callIndex, CaptureCreateUserEvent(true, context, nullptr, event)); callIndex += 2; break; } } callVector->erase(callVector->begin() + callIndex); } void FrameCaptureShared::maybeCapturePreCallUpdatesCL(CallCapture &call) { switch (call.entryPoint) { case EntryPoint::CLGetExtensionFunctionAddress: case EntryPoint::CLGetExtensionFunctionAddressForPlatform: { uint32_t index = call.entryPoint == EntryPoint::CLGetExtensionFunctionAddress ? 0 : 1; std::string funcName = (const char *)call.params.getParam("func_name", ParamType::TcharConstPointer, index) .value.charConstPointerPointerVal; call.customFunctionName = funcName + " = (" + funcName + "_fn)" + GetEntryPointName(call.entryPoint); if (std::find(mExtFuncsAdded.begin(), mExtFuncsAdded.end(), funcName) == mExtFuncsAdded.end()) { mExtFuncsAdded.push_back(funcName); } break; } case EntryPoint::CLCreateContext: case EntryPoint::CLCreateContextFromType: { if (call.params.getParam("properties", ParamType::Tcl_context_propertiesConstPointer, 0) .value.cl_context_propertiesConstPointerVal) { size_t propSize = 0; size_t platformIDIndex = 0; const cl_context_properties *propertiesData = (cl_context_properties *)call.params .getParam("properties", ParamType::Tcl_context_propertiesConstPointer, 0) .data[0] .data(); while (propertiesData[propSize] != 0) { if (propertiesData[propSize] == CL_CONTEXT_PLATFORM) { // "Each property name is immediately followed by the corresponding desired // value" platformIDIndex = propSize + 1; } ++propSize; } ++propSize; if (platformIDIndex == 0) { ParamBuffer params; params.addValueParam("propSize", ParamType::Tsize_t, propSize); ParamCapture propertiesParam("propData", ParamType::Tcl_context_propertiesConstPointer); InitParamValue(ParamType::Tcl_context_propertiesConstPointer, propertiesData, &propertiesParam.value); CaptureMemory(propertiesData, propSize * sizeof(cl_context_properties), &propertiesParam); params.addParam(std::move(propertiesParam)); mFrameCalls.emplace_back( CallCapture("UpdateCLContextPropertiesNoPlatform", std::move(params))); call.params .getParam("properties", ParamType::Tcl_context_propertiesConstPointer, 0) .data.clear(); break; } // Create call to UpdateCLContextProperties ParamBuffer params; params.addValueParam("propSize", ParamType::Tsize_t, propSize); ParamCapture propertiesParam("propData", ParamType::Tcl_context_propertiesConstPointer); InitParamValue(ParamType::Tcl_context_propertiesConstPointer, propertiesData, &propertiesParam.value); CaptureMemory(propertiesData, propSize * sizeof(cl_context_properties), &propertiesParam); params.addParam(std::move(propertiesParam)); params.addValueParam("platformIdxInProps", ParamType::Tsize_t, platformIDIndex); params.addValueParam("platformIdxInMap", ParamType::Tsize_t, getIndex((cl_platform_id *)&propertiesData[platformIDIndex])); call.params.getParam("properties", ParamType::Tcl_context_propertiesConstPointer, 0) .data.clear(); mFrameCalls.emplace_back( CallCapture("UpdateCLContextPropertiesWithPlatform", std::move(params))); } break; } default: break; } updateReadBufferSize(call.params.getReadBufferSize()); } void FrameCaptureShared::addCLResetObj(const ParamCapture ¶m) { mResourceTrackerCL.mCLResetObjs.push_back(angle::ParamCapture("resetObj", param.type)); auto paramValue = &mResourceTrackerCL.mCLResetObjs.at(mResourceTrackerCL.mCLResetObjs.size() - 1).value; switch (param.type) { case ParamType::Tcl_device_id: InitParamValue(param.type, param.value.cl_device_idVal, paramValue); break; case ParamType::Tcl_mem: InitParamValue(param.type, param.value.cl_memVal, paramValue); break; case ParamType::Tcl_kernel: InitParamValue(param.type, param.value.cl_kernelVal, paramValue); break; case ParamType::Tcl_program: InitParamValue(param.type, param.value.cl_programVal, paramValue); break; case ParamType::Tcl_command_queue: InitParamValue(param.type, param.value.cl_command_queueVal, paramValue); break; case ParamType::Tcl_context: InitParamValue(param.type, param.value.cl_contextVal, paramValue); break; case ParamType::Tcl_sampler: InitParamValue(param.type, param.value.cl_samplerVal, paramValue); break; case ParamType::Tcl_event: InitParamValue(param.type, param.value.cl_eventVal, paramValue); break; default: break; } } void FrameCaptureShared::removeCLResetObj(const ParamCapture ¶m) { std::vector &mCLResetObjs = mResourceTrackerCL.mCLResetObjs; for (size_t i = 0; i < mCLResetObjs.size(); ++i) { bool foundCLObj = param.type == mCLResetObjs.at(i).type && ((param.type == ParamType::Tcl_device_id && param.value.cl_device_idVal == mCLResetObjs.at(i).value.cl_device_idVal) || (param.type == ParamType::Tcl_mem && param.value.cl_memVal == mCLResetObjs.at(i).value.cl_memVal) || (param.type == ParamType::Tcl_kernel && param.value.cl_kernelVal == mCLResetObjs.at(i).value.cl_kernelVal) || (param.type == ParamType::Tcl_program && param.value.cl_programVal == mCLResetObjs.at(i).value.cl_programVal) || (param.type == ParamType::Tcl_command_queue && param.value.cl_command_queueVal == mCLResetObjs.at(i).value.cl_command_queueVal) || (param.type == ParamType::Tcl_context && param.value.cl_contextVal == mCLResetObjs.at(i).value.cl_contextVal) || (param.type == ParamType::Tcl_sampler && param.value.cl_samplerVal == mCLResetObjs.at(i).value.cl_samplerVal) || (param.type == ParamType::Tcl_event && param.value.cl_eventVal == mCLResetObjs.at(i).value.cl_eventVal)); if (foundCLObj) { mCLResetObjs.erase(mCLResetObjs.begin() + i); break; } } } void FrameCaptureShared::printCLResetObjs(std::stringstream &stream) { std::vector &mCLResetObjs = mResourceTrackerCL.mCLResetObjs; for (size_t i = 0; i < mCLResetObjs.size(); ++i) { stream << " "; switch (mCLResetObjs.at(i).type) { case ParamType::Tcl_device_id: stream << "clReleaseDevice(clDevicesMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_device_idVal)) << "]);"; break; case ParamType::Tcl_mem: stream << "clReleaseMemObject(clMemMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_memVal)) << "]);"; break; case ParamType::Tcl_kernel: stream << "clReleaseKernel(clKernelsMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_kernelVal)) << "]);"; break; case ParamType::Tcl_program: stream << "clReleaseProgram(clProgramsMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_programVal)) << "]);"; break; case ParamType::Tcl_command_queue: stream << "clReleaseCommandQueue(clCommandQueuesMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_command_queueVal)) << "]);"; break; case ParamType::Tcl_context: stream << "clReleaseContext(clContextsMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_contextVal)) << "]);"; break; case ParamType::Tcl_sampler: stream << "clReleaseSampler(clSamplersMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_samplerVal)) << "]);"; break; case ParamType::Tcl_event: stream << "clReleaseEvent(clEventsMap[" << std::to_string(getIndex(&mCLResetObjs.at(i).value.cl_eventVal)) << "]);"; break; default: break; } stream << "\n"; } } void FrameCaptureShared::updateResourceCountsFromParamCaptureCL(const ParamCapture ¶m, const CallCapture &call) { switch (param.type) { case ParamType::Tcl_platform_idPointer: if (call.entryPoint == EntryPoint::CLIcdGetPlatformIDsKHR || call.entryPoint == EntryPoint::CLGetPlatformIDs) { mMaxCLParamsSize[param.type] += sizeof(cl_platform_id) * ((call.params.getParamCaptures()[0]).value.cl_uintVal); } break; case ParamType::Tcl_device_idPointer: if (call.entryPoint == EntryPoint::CLGetDeviceIDs) { mMaxCLParamsSize[param.type] += sizeof(cl_device_id) * ((call.params.getParamCaptures()[2]).value.cl_uintVal); } break; case ParamType::Tcl_context: if (call.entryPoint == EntryPoint::CLCreateContext || call.entryPoint == EntryPoint::CLCreateContextFromType) { if ((getIndex(¶m.value.cl_contextVal) + 1) * sizeof(cl_context) > mMaxCLParamsSize[param.type]) { mMaxCLParamsSize[param.type] = (uint32_t)((getIndex(¶m.value.cl_contextVal) + 1) * sizeof(cl_context)); } addCLResetObj(param); } break; case ParamType::Tcl_command_queue: if (call.entryPoint == EntryPoint::CLCreateCommandQueueWithProperties || call.entryPoint == EntryPoint::CLCreateCommandQueue) { if ((getIndex(¶m.value.cl_command_queueVal) + 1) * sizeof(cl_command_queue) > mMaxCLParamsSize[param.type]) { mMaxCLParamsSize[param.type] = (uint32_t)((getIndex(¶m.value.cl_command_queueVal) + 1) * sizeof(cl_command_queue)); } addCLResetObj(param); } break; case ParamType::Tcl_mem: if (call.entryPoint == EntryPoint::CLCreateBufferWithProperties || call.entryPoint == EntryPoint::CLCreateBuffer || call.entryPoint == EntryPoint::CLCreateSubBuffer || call.entryPoint == EntryPoint::CLCreateImageWithProperties || call.entryPoint == EntryPoint::CLCreateImage || call.entryPoint == EntryPoint::CLCreateImage2D || call.entryPoint == EntryPoint::CLCreateImage3D) { if ((getIndex(¶m.value.cl_memVal) + 1) * sizeof(cl_mem) > mMaxCLParamsSize[param.type]) { mMaxCLParamsSize[param.type] = (uint32_t)((getIndex(¶m.value.cl_memVal) + 1) * sizeof(cl_mem)); } addCLResetObj(param); } break; case ParamType::Tcl_eventPointer: { if (param.value.cl_eventVal) { if ((getIndex(¶m.value.cl_eventVal) + 1) * sizeof(cl_event) > mMaxCLParamsSize[param.type]) { mMaxCLParamsSize[param.type] = (uint32_t)((getIndex(¶m.value.cl_eventVal) + 1) * sizeof(cl_event)); } angle::ParamCapture eventParam("event", angle::ParamType::Tcl_event); InitParamValue(angle::ParamType::Tcl_event, param.value.cl_eventVal, &eventParam.value); addCLResetObj(eventParam); } break; } case ParamType::Tcl_program: if (call.entryPoint == EntryPoint::CLCreateProgramWithSource || call.entryPoint == EntryPoint::CLCreateProgramWithBinary || call.entryPoint == EntryPoint::CLCreateProgramWithBuiltInKernels || call.entryPoint == EntryPoint::CLLinkProgram || call.entryPoint == EntryPoint::CLCreateProgramWithIL) { if ((getIndex(¶m.value.cl_programVal) + 1) * sizeof(cl_program) > mMaxCLParamsSize[param.type]) { mMaxCLParamsSize[param.type] = (uint32_t)((getIndex(¶m.value.cl_programVal) + 1) * sizeof(cl_program)); } addCLResetObj(param); } break; case ParamType::Tcl_kernel: if (call.entryPoint == EntryPoint::CLCreateKernel || call.entryPoint == EntryPoint::CLCloneKernel) { if ((getIndex(¶m.value.cl_kernelVal) + 1) * sizeof(cl_kernel) > mMaxCLParamsSize[param.type]) { mMaxCLParamsSize[param.type] = (uint32_t)((getIndex(¶m.value.cl_kernelVal) + 1) * sizeof(cl_kernel)); } addCLResetObj(param); } break; case ParamType::Tcl_sampler: if (call.entryPoint == EntryPoint::CLCreateSampler || call.entryPoint == EntryPoint::CLCreateSamplerWithProperties) { if ((getIndex(¶m.value.cl_samplerVal) + 1) * sizeof(cl_sampler) > mMaxCLParamsSize[param.type]) { mMaxCLParamsSize[param.type] = (uint32_t)((getIndex(¶m.value.cl_samplerVal) + 1) * sizeof(cl_sampler)); } addCLResetObj(param); } break; case ParamType::TvoidPointer: if (call.entryPoint == EntryPoint::CLEnqueueMapImage || call.entryPoint == EntryPoint::CLEnqueueMapBuffer) { mMaxCLParamsSize[param.type] += sizeof(void *); } break; default: break; } } void FrameCaptureShared::updateResourceCountsFromCallCaptureCL(const CallCapture &call) { for (const ParamCapture ¶m : call.params.getParamCaptures()) { updateResourceCountsFromParamCaptureCL(param, call); } // Update resource IDs in the return value. switch (call.entryPoint) { case EntryPoint::CLCreateContext: case EntryPoint::CLCreateContextFromType: setIndex(&call.params.getReturnValue().value.cl_contextVal); updateResourceCountsFromParamCaptureCL(call.params.getReturnValue(), call); break; case EntryPoint::CLCreateBuffer: case EntryPoint::CLCreateBufferWithProperties: case EntryPoint::CLCreateSubBuffer: case EntryPoint::CLCreateImageWithProperties: case EntryPoint::CLCreateImage: case EntryPoint::CLCreateImage2D: case EntryPoint::CLCreateImage3D: case EntryPoint::CLCreatePipe: setIndex(&call.params.getReturnValue().value.cl_memVal); updateResourceCountsFromParamCaptureCL(call.params.getReturnValue(), call); break; case EntryPoint::CLCreateSampler: case EntryPoint::CLCreateSamplerWithProperties: setIndex(&call.params.getReturnValue().value.cl_samplerVal); updateResourceCountsFromParamCaptureCL(call.params.getReturnValue(), call); break; case EntryPoint::CLCreateCommandQueue: case EntryPoint::CLCreateCommandQueueWithProperties: setIndex(&call.params.getReturnValue().value.cl_command_queueVal); updateResourceCountsFromParamCaptureCL(call.params.getReturnValue(), call); break; case EntryPoint::CLCreateProgramWithSource: case EntryPoint::CLCreateProgramWithBinary: case EntryPoint::CLCreateProgramWithBuiltInKernels: case EntryPoint::CLLinkProgram: case EntryPoint::CLCreateProgramWithIL: setIndex(&call.params.getReturnValue().value.cl_programVal); updateResourceCountsFromParamCaptureCL(call.params.getReturnValue(), call); break; case EntryPoint::CLCreateKernel: case EntryPoint::CLCloneKernel: setIndex(&call.params.getReturnValue().value.cl_kernelVal); updateResourceCountsFromParamCaptureCL(call.params.getReturnValue(), call); break; case EntryPoint::CLEnqueueMapBuffer: case EntryPoint::CLEnqueueMapImage: case EntryPoint::CLSVMAlloc: if (call.params.getReturnValue().value.voidPointerVal) { setCLVoidIndex(call.params.getReturnValue().value.voidPointerVal); updateResourceCountsFromParamCaptureCL(call.params.getReturnValue(), call); } break; case EntryPoint::CLCreateUserEvent: setIndex(&call.params.getReturnValue().value.cl_eventVal); break; case EntryPoint::CLReleaseDevice: case EntryPoint::CLReleaseCommandQueue: case EntryPoint::CLReleaseContext: case EntryPoint::CLReleaseEvent: case EntryPoint::CLReleaseKernel: case EntryPoint::CLReleaseMemObject: case EntryPoint::CLReleaseProgram: case EntryPoint::CLReleaseSampler: removeCLResetObj(call.params.getParamCaptures()[0]); break; default: break; } } void FrameCaptureShared::captureCLCall(CallCapture &&inCall, bool isCallValid) { if (!mCallCaptured) { mReplayWriter.captureAPI = CaptureAPI::CL; mBinaryData.clear(); mCallCaptured = true; std::atexit(onCLProgramEnd); } if (mFrameIndex <= mCaptureEndFrame) { // Keep track of return values from OpenCL calls updateResourceCountsFromCallCaptureCL(inCall); // Set to true if the call signifies the end of a frame // ex: clEnqueueNDRangeKernel bool frameEnd = false; // Covers pre call updates, like updating the read buffer size maybeCapturePreCallUpdatesCL(inCall); // If it's an unnecessary call for replay (ex: clGetDeviceInfo) if (mCLOptionalCalls.find(inCall.entryPoint) == mCLOptionalCalls.end()) { if (mCLEndFrameCalls.find(inCall.entryPoint) != mCLEndFrameCalls.end()) { frameEnd = true; } mFrameCalls.emplace_back(std::move(inCall)); } else { saveCLGetInfo(inCall); return; } // For kernel argument memory snapshots maybeCapturePostCallUpdatesCL(); if (mFrameIndex >= mCaptureStartFrame || (mFrameIndex + 1 == mCaptureStartFrame && frameEnd)) { // Maybe add clEnqueueWrite* or memcpy for memory snapshots captureUpdateCLObjs(&mFrameCalls); } if (frameEnd && mFrameIndex >= mCaptureStartFrame) { mActiveFrameIndices.push_back(mFrameIndex); writeMainContextCppReplayCL(); if (mFrameIndex == mCaptureEndFrame) { writeCppReplayIndexFilesCL(); SaveBinaryData(mCompression, mOutDirectory, kNoContextId, mCaptureLabel, mBinaryData); } reset(); } if (frameEnd) { if (mFrameIndex == (mCaptureStartFrame == 0 ? 0 : mCaptureStartFrame - 1)) { mCLSetupCalls = std::move(mFrameCalls); } ++mFrameIndex; } } } void FrameCaptureShared::maybeCapturePostCallUpdatesCL() { CallCapture &lastCall = mFrameCalls.back(); switch (lastCall.entryPoint) { case EntryPoint::CLEnqueueMapBuffer: { // Recreate the map call to store in the mCLMapCall unordered_map // so later upon the unmap call, the original map data will be available cl_command_queue command_queue = lastCall.params.getParam("command_queue", ParamType::Tcl_command_queue, 0) .value.cl_command_queueVal; cl_mem buffer = lastCall.params.getParam("buffer", ParamType::Tcl_mem, 1).value.cl_memVal; cl_bool blocking_map = lastCall.params.getParam("blocking_map", ParamType::Tcl_bool, 2).value.cl_boolVal; cl::MapFlags map_flags = lastCall.params.getParam("map_flagsPacked", ParamType::TMapFlags, 3) .value.MapFlagsVal; size_t offset = lastCall.params.getParam("offset", ParamType::Tsize_t, 4).value.size_tVal; size_t size = lastCall.params.getParam("size", ParamType::Tsize_t, 5).value.size_tVal; mResourceTrackerCL.mCLMapCall.emplace( lastCall.params.getReturnValue().value.voidPointerVal, CaptureEnqueueMapBuffer(true, command_queue, buffer, blocking_map, map_flags, offset, size, 0, nullptr, nullptr, nullptr, nullptr)); break; } case EntryPoint::CLEnqueueMapImage: { // Recreate the map call to store in the mCLMapCall unordered_map // so later upon the unmap call, the original map data will be available cl_command_queue command_queue = lastCall.params.getParam("command_queue", ParamType::Tcl_command_queue, 0) .value.cl_command_queueVal; cl_mem image = lastCall.params.getParam("image", ParamType::Tcl_mem, 1).value.cl_memVal; cl_bool blocking_map = lastCall.params.getParam("blocking_map", ParamType::Tcl_bool, 2).value.cl_boolVal; cl::MapFlags map_flags = lastCall.params.getParam("map_flagsPacked", ParamType::TMapFlags, 3) .value.MapFlagsVal; const size_t *origin = lastCall.params.getParam("origin", ParamType::Tsize_tConstPointer, 4) .value.size_tConstPointerVal; const size_t *region = lastCall.params.getParam("region", ParamType::Tsize_tConstPointer, 5) .value.size_tConstPointerVal; size_t *image_row_pitch = lastCall.params.getParam("image_row_pitch", ParamType::Tsize_tPointer, 6) .value.size_tPointerVal; size_t *image_slice_pitch = lastCall.params.getParam("image_slice_pitch", ParamType::Tsize_tPointer, 7) .value.size_tPointerVal; mResourceTrackerCL.mCLMapCall.emplace( lastCall.params.getReturnValue().value.voidPointerVal, CaptureEnqueueMapImage(true, command_queue, image, blocking_map, map_flags, origin, region, image_row_pitch, image_slice_pitch, 0, nullptr, nullptr, nullptr, nullptr)); mResourceTrackerCL.mCLMapCall.at(lastCall.params.getReturnValue().value.voidPointerVal) .params.setValueParamAtIndex("image_row_pitch", ParamType::Tsize_t, *image_row_pitch, 6); mResourceTrackerCL.mCLMapCall.at(lastCall.params.getReturnValue().value.voidPointerVal) .params.setValueParamAtIndex("image_slice_pitch", ParamType::Tsize_t, image_slice_pitch == nullptr ? 0 : *image_slice_pitch, 7); break; } case EntryPoint::CLEnqueueUnmapMemObject: { if (mFrameIndex >= mCaptureStartFrame) { // Mark as dirty cl_mem *mem = &lastCall.params.getParam("memobj", ParamType::Tcl_mem, 1).value.cl_memVal; mResourceTrackerCL.mCLCurrentCommandQueue = lastCall.params.getParam("command_queue", ParamType::Tcl_command_queue, 0) .value.cl_command_queueVal; CallCapture *mapCall = &mResourceTrackerCL.mCLMapCall.at( lastCall.params.getParam("mapped_ptr", ParamType::TvoidPointer, 2) .value.voidPointerVal); auto it = std::find(mResourceTrackerCL.mCLDirtyMem.begin(), mResourceTrackerCL.mCLDirtyMem.end(), *mem); if (it == mResourceTrackerCL.mCLDirtyMem.end() && mapCall->params.getParam("map_flagsPacked", ParamType::TMapFlags, 3) .value.MapFlagsVal.mask(CL_MAP_WRITE | CL_MAP_WRITE_INVALIDATE_REGION) != 0u) { mResourceTrackerCL.mCLDirtyMem.push_back(*mem); } } break; } case EntryPoint::CLEnqueueSVMUnmap: { // Mark as dirty void *svm = &lastCall.params.getParam("svm_ptr", ParamType::TvoidPointer, 1) .value.voidPointerVal; mResourceTrackerCL.mCLCurrentCommandQueue = lastCall.params.getParam("command_queue", ParamType::Tcl_command_queue, 0) .value.cl_command_queueVal; mResourceTrackerCL.mCLDirtySVM.push_back(svm); break; } default: break; } // OpenCL calls that come before the starting frame if (mFrameIndex < mCaptureStartFrame) { std::unordered_map> &mCLProgramToKernels = mResourceTrackerCL.mCLProgramToKernels; switch (lastCall.entryPoint) { // There should be no unnecessary enqueue functions prior to the starting frame. // captureUpdateCLObjs accounts for it by dynamically adding // CLEnqueueWriteBuffer/CLEnqueueWriteImage to ensure the cl_mem objects // have the needed info upon replay case EntryPoint::CLEnqueueNDRangeKernel: case EntryPoint::CLEnqueueNativeKernel: case EntryPoint::CLEnqueueTask: case EntryPoint::CLEnqueueReadBuffer: case EntryPoint::CLEnqueueWriteBuffer: case EntryPoint::CLEnqueueReadBufferRect: case EntryPoint::CLEnqueueWriteBufferRect: case EntryPoint::CLEnqueueReadImage: case EntryPoint::CLEnqueueWriteImage: case EntryPoint::CLEnqueueCopyBuffer: case EntryPoint::CLEnqueueCopyBufferRect: case EntryPoint::CLEnqueueCopyImage: case EntryPoint::CLEnqueueCopyBufferToImage: case EntryPoint::CLEnqueueCopyImageToBuffer: case EntryPoint::CLEnqueueFillBuffer: case EntryPoint::CLEnqueueFillImage: case EntryPoint::CLEnqueueWaitForEvents: case EntryPoint::CLEnqueueMarkerWithWaitList: case EntryPoint::CLEnqueueBarrierWithWaitList: case EntryPoint::CLEnqueueBarrier: case EntryPoint::CLEnqueueMarker: case EntryPoint::CLEnqueueMigrateMemObjects: case EntryPoint::CLEnqueueSVMMemcpy: case EntryPoint::CLEnqueueSVMMemFill: case EntryPoint::CLEnqueueSVMMigrateMem: { size_t index = mFrameCalls.size() - 1; removeCLCall(&mFrameCalls, index); break; } case EntryPoint::CLCreateBuffer: case EntryPoint::CLCreateBufferWithProperties: case EntryPoint::CLCreateImage: case EntryPoint::CLCreateImageWithProperties: case EntryPoint::CLCreateImage2D: case EntryPoint::CLCreateImage3D: case EntryPoint::CLCreatePipe: case EntryPoint::CLCreateSubBuffer: { const cl_mem *newBuff = &lastCall.params.getReturnValue().value.cl_memVal; // Set the parent if (lastCall.entryPoint == EntryPoint::CLCreateSubBuffer) { cl_mem parent = lastCall.params.getParam("buffer", ParamType::Tcl_mem, 0).value.cl_memVal; mResourceTrackerCL.mCLSubBufferToParent[*newBuff] = parent; } // Implicit retain (*newBuff)->cast().retain(); // Add buffer as tracked trackCLMemUpdate(newBuff, true); break; } case EntryPoint::CLReleaseMemObject: { // Potentially remove buffer/image (and potentially parents) as tracked trackCLMemUpdate( &lastCall.params.getParam("memobj", ParamType::Tcl_mem, 0).value.cl_memVal, false); break; } case EntryPoint::CLCreateCommandQueue: case EntryPoint::CLCreateCommandQueueWithProperties: { mResourceTrackerCL.mCLCurrentCommandQueue = lastCall.params.getReturnValue().value.cl_command_queueVal; break; } case EntryPoint::CLCreateProgramWithSource: case EntryPoint::CLCreateProgramWithBinary: case EntryPoint::CLCreateProgramWithBuiltInKernels: case EntryPoint::CLCreateProgramWithIL: { mCLProgramToKernels[lastCall.params.getReturnValue().value.cl_programVal] = std::vector(); trackCLProgramUpdate(&lastCall.params.getReturnValue().value.cl_programVal, true, 0, nullptr); break; } case EntryPoint::CLRetainProgram: { trackCLProgramUpdate(&lastCall.params.getParam("program", ParamType::Tcl_program, 0) .value.cl_programVal, true, 0, nullptr); break; } case EntryPoint::CLCompileProgram: { const cl_program *program = &lastCall.params.getParam("program", ParamType::Tcl_program, 0) .value.cl_programVal; trackCLProgramUpdate( program, true, lastCall.params.getParam("num_input_headers", ParamType::Tcl_uint, 4) .value.cl_uintVal, lastCall.params.getParam("input_headers", ParamType::Tcl_programConstPointer, 5) .value.cl_programConstPointerVal); break; } case EntryPoint::CLLinkProgram: { const cl_program *program = &lastCall.params.getReturnValue().value.cl_programVal; mCLProgramToKernels[*program] = std::vector(); trackCLProgramUpdate( program, true, lastCall.params.getParam("num_input_programs", ParamType::Tcl_uint, 4) .value.cl_uintVal, lastCall.params .getParam("input_programs", ParamType::Tcl_programConstPointer, 5) .value.cl_programConstPointerVal); break; } case EntryPoint::CLReleaseProgram: { trackCLProgramUpdate(&lastCall.params.getParam("program", ParamType::Tcl_program, 0) .value.cl_programVal, false, 0, nullptr); break; } case EntryPoint::CLCreateKernel: { cl_program *program = &lastCall.params.getParam("program", ParamType::Tcl_program, 0) .value.cl_programVal; const cl_kernel *kernel = &lastCall.params.getReturnValue().value.cl_kernelVal; mCLProgramToKernels[*program].push_back(*kernel); mResourceTrackerCL.mCLKernelToProgram[*kernel] = *program; break; } case EntryPoint::CLCloneKernel: { cl_program *program = &mResourceTrackerCL.mCLKernelToProgram[lastCall.params .getParam("source_kernel", ParamType::Tcl_kernel, 0) .value.cl_kernelVal]; const cl_kernel *kernel = &lastCall.params.getReturnValue().value.cl_kernelVal; mCLProgramToKernels[*program].push_back(*kernel); mResourceTrackerCL.mCLKernelToProgram[*kernel] = *program; break; } case EntryPoint::CLSVMAlloc: { void *svm = lastCall.params.getReturnValue().value.voidPointerVal; // Potentially mark as dirty auto it = std::find(mResourceTrackerCL.mCLDirtySVM.begin(), mResourceTrackerCL.mCLDirtySVM.end(), svm); if (it == mResourceTrackerCL.mCLDirtySVM.end()) { mResourceTrackerCL.mCLDirtySVM.push_back(svm); } break; } case EntryPoint::CLSVMFree: { void *svm = lastCall.params.getParam("svm_pointer", ParamType::TvoidPointer, 1) .value.voidPointerVal; auto it = std::find(mResourceTrackerCL.mCLDirtySVM.begin(), mResourceTrackerCL.mCLDirtySVM.end(), svm); if (it != mResourceTrackerCL.mCLDirtySVM.end()) { mResourceTrackerCL.mCLDirtySVM.erase(it); } break; } case EntryPoint::CLEnqueueSVMFree: { for (cl_uint svmIndex = 0; svmIndex < lastCall.params.getParam("num_svm_pointers", ParamType::Tcl_uint, 1) .value.cl_uintVal; ++svmIndex) { void *svm = lastCall.params.getParam("svm_pointers", ParamType::TvoidPointerPointer, 1) .value.voidPointerPointerVal[svmIndex]; auto it = std::find(mResourceTrackerCL.mCLDirtySVM.begin(), mResourceTrackerCL.mCLDirtySVM.end(), svm); if (it != mResourceTrackerCL.mCLDirtySVM.end()) { mResourceTrackerCL.mCLDirtySVM.erase(it); } } break; } default: break; } } } void FrameCaptureShared::onCLProgramEnd() { if (cl::Platform::GetDefault()->getFrameCaptureShared()->onEndCLCapture()) { delete cl::Platform::GetDefault()->getFrameCaptureShared(); } } bool FrameCaptureShared::onEndCLCapture() { if (mFrameIndex >= mCaptureStartFrame && mFrameIndex <= mCaptureEndFrame) { mActiveFrameIndices.push_back(mFrameIndex); mCaptureEndFrame = mFrameIndex; writeMainContextCppReplayCL(); writeCppReplayIndexFilesCL(); SaveBinaryData(mCompression, mOutDirectory, kNoContextId, mCaptureLabel, mBinaryData); return true; } return false; } ResourceTrackerCL::ResourceTrackerCL() = default; ResourceTrackerCL::~ResourceTrackerCL() = default; void FrameCaptureShared::setCLPlatformIndices(cl_platform_id *platforms, size_t numPlatforms) { for (uint32_t i = 0; i < numPlatforms; ++i) { setIndex(&platforms[i]); } } void FrameCaptureShared::setCLDeviceIndices(cl_device_id *devices, size_t numDevices) { for (uint32_t i = 0; i < numDevices; ++i) { setIndex(&devices[i]); } } size_t FrameCaptureShared::getCLVoidIndex(const void *v) { if (mResourceTrackerCL.mCLVoidIndices.find(v) == mResourceTrackerCL.mCLVoidIndices.end()) { return SIZE_MAX; } return mResourceTrackerCL.mCLVoidIndices[v]; } void FrameCaptureShared::setCLVoidIndex(const void *v) { if (mResourceTrackerCL.mCLVoidIndices.find(v) == mResourceTrackerCL.mCLVoidIndices.end()) { size_t tempSize = mResourceTrackerCL.mCLVoidIndices.size(); mResourceTrackerCL.mCLVoidIndices[v] = tempSize; } } void FrameCaptureShared::setCLVoidVectorIndex(const void *pointers[], size_t numPointers, const angle::ParamCapture *paramCaptureKey) { mResourceTrackerCL.mCLParamIDToIndexVector[paramCaptureKey->uniqueID] = std::vector(); for (size_t i = 0; i < numPointers; ++i) { mResourceTrackerCL.mCLParamIDToIndexVector[paramCaptureKey->uniqueID].push_back( getCLVoidIndex(pointers[i])); } } void FrameCaptureShared::setOffsetsVector(const void *args, const void **argsLocations, size_t numLocations, const angle::ParamCapture *paramCaptureKey) { mResourceTrackerCL.mCLParamIDToIndexVector[paramCaptureKey->uniqueID] = std::vector(); for (size_t i = 0; i < numLocations; ++i) { mResourceTrackerCL.mCLParamIDToIndexVector[paramCaptureKey->uniqueID].push_back( (char *)argsLocations[i] - (char *)args); } } std::vector FrameCaptureShared::getCLObjVector(const angle::ParamCapture *paramCaptureKey) { if (mResourceTrackerCL.mCLParamIDToIndexVector.find(paramCaptureKey->uniqueID) != mResourceTrackerCL.mCLParamIDToIndexVector.end()) { return mResourceTrackerCL.mCLParamIDToIndexVector[paramCaptureKey->uniqueID]; } return std::vector(); } template std::unordered_map &FrameCaptureShared::getMap() { ASSERT(false); return std::unordered_map(); } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLPlatformIDIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLDeviceIDIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLContextIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLEventsIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLCommandQueueIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLMemIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLSamplerIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLProgramIndices; } template <> std::unordered_map &FrameCaptureShared::getMap() { return mResourceTrackerCL.mCLKernelIndices; } void FrameCaptureShared::writeJSONCL() { JsonSerializer json; json.startGroup("TraceMetadata"); json.addBool("IsBinaryDataCompressed", mCompression); json.addScalar("CaptureRevision", GetANGLERevision()); json.addScalar("FrameStart", mCaptureStartFrame); json.addScalar("FrameEnd", mFrameIndex); json.addBool("IsOpenCL", true); json.endGroup(); { const std::vector &traceFiles = mReplayWriter.getAndResetWrittenFiles(); json.addVectorOfStrings("TraceFiles", traceFiles); } { std::stringstream jsonFileNameStream; jsonFileNameStream << mOutDirectory << FmtCapturePrefix(kNoContextId, mCaptureLabel) << ".json"; std::string jsonFileName = jsonFileNameStream.str(); SaveFileHelper saveData(jsonFileName); saveData.write(reinterpret_cast(json.data()), json.length()); } } void FrameCaptureShared::saveCLGetInfo(const CallCapture &call) { std::string prevCall = ""; size_t size; std::ostringstream objectStream; std::string clObject; JsonSerializer json; json.startGroup(call.name()); // Below ONLY for clGetSupportedImageFormats if (call.entryPoint == EntryPoint::CLGetSupportedImageFormats) { const cl_image_format *data = call.params.getParam("image_formats", ParamType::Tcl_image_formatPointer, 4) .value.cl_image_formatPointerVal; if (!data) { return; } size_t *sizePointer = call.params.getParam("num_image_formats", ParamType::Tcl_uintPointer, 5) .value.size_tPointerVal; if (!sizePointer) { size = call.params.getParam("param_value_size", ParamType::Tcl_uint, 3).value.cl_uintVal; } else { size = *sizePointer; } cl_context context = call.params.getParamCaptures()[0].value.cl_contextVal; objectStream << static_cast(context); clObject = objectStream.str(); json.addString("context", clObject); json.addScalar("flags", call.params.getParamCaptures()[1].value.MemFlagsVal.get()); cl::MemObjectType imageType = call.params.getParam("image_typePacked", ParamType::TMemObjectType, 2) .value.MemObjectTypeVal; std::ostringstream oss; oss << imageType; std::string infoString = oss.str(); json.startGroup(infoString); for (size_t j = 0; j < size; ++j) { std::ostringstream temp; temp << (j + 1); json.addScalar("image_channel_order" + temp.str(), data[j].image_channel_order); json.addScalar("image_channel_data_type" + temp.str(), data[j].image_channel_data_type); } json.endGroup(); json.endGroup(); return; } // Get the param_value and size bool offsetData = 0; switch (call.entryPoint) { case EntryPoint::CLGetProgramBuildInfo: case EntryPoint::CLGetKernelArgInfo: case EntryPoint::CLGetKernelWorkGroupInfo: { offsetData = 1; break; } default: break; } const void *data = call.params.getParam("param_value", ParamType::TvoidPointer, 3 + offsetData) .value.voidPointerVal; if (!data) { return; } size_t *sizePointer = call.params.getParam("param_value_size_ret", ParamType::Tsize_tPointer, 4 + offsetData) .value.size_tPointerVal; if (!sizePointer) { size = call.params.getParam("param_value_size", ParamType::Tsize_t, 2 + offsetData) .value.size_tVal; } else { size = *sizePointer; } // Get string representation of OpenCL object specified switch (call.entryPoint) { case EntryPoint::CLGetPlatformInfo: { cl_platform_id platform = call.params.getParamCaptures()[0].value.cl_platform_idVal; objectStream << static_cast(platform); break; } case EntryPoint::CLGetDeviceInfo: { cl_device_id device = call.params.getParamCaptures()[0].value.cl_device_idVal; objectStream << static_cast(device); break; } case EntryPoint::CLGetContextInfo: { cl_context context = call.params.getParamCaptures()[0].value.cl_contextVal; objectStream << static_cast(context); break; } case EntryPoint::CLGetCommandQueueInfo: { cl_command_queue commandQueue = call.params.getParamCaptures()[0].value.cl_command_queueVal; objectStream << static_cast(commandQueue); break; } case EntryPoint::CLGetProgramInfo: case EntryPoint::CLGetProgramBuildInfo: { cl_program program = call.params.getParamCaptures()[0].value.cl_programVal; objectStream << static_cast(program); break; } case EntryPoint::CLGetKernelInfo: case EntryPoint::CLGetKernelArgInfo: case EntryPoint::CLGetKernelWorkGroupInfo: { cl_kernel kernel = call.params.getParamCaptures()[0].value.cl_kernelVal; objectStream << static_cast(kernel); break; } case EntryPoint::CLGetEventInfo: case EntryPoint::CLGetEventProfilingInfo: { cl_event event = call.params.getParamCaptures()[0].value.cl_eventVal; objectStream << static_cast(event); break; } case EntryPoint::CLGetMemObjectInfo: case EntryPoint::CLGetImageInfo: { cl_mem mem = call.params.getParamCaptures()[0].value.cl_memVal; objectStream << static_cast(mem); break; } case EntryPoint::CLGetSamplerInfo: { cl_sampler sampler = call.params.getParamCaptures()[0].value.cl_samplerVal; objectStream << static_cast(sampler); break; } default: break; } clObject = objectStream.str(); // Go through the param_name options switch (call.entryPoint) { case EntryPoint::CLGetPlatformInfo: { cl::PlatformInfo info = call.params.getParamCaptures()[1].value.PlatformInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("platform", clObject); switch (ToCLenum(info)) { case CL_PLATFORM_PROFILE: case CL_PLATFORM_VERSION: case CL_PLATFORM_NAME: case CL_PLATFORM_VENDOR: case CL_PLATFORM_EXTENSIONS: case CL_PLATFORM_ICD_SUFFIX_KHR: { json.addCString(infoString, static_cast(data)); break; } case CL_PLATFORM_EXTENSIONS_WITH_VERSION: { const cl_name_version *nameVersion = static_cast(data); json.startGroup(infoString); for (size_t j = 0; j < size / sizeof(cl_name_version); ++j) { json.addScalar(nameVersion[j].name, nameVersion[j].version); } json.endGroup(); break; } case CL_PLATFORM_NUMERIC_VERSION: json.addScalar(infoString, *static_cast(data)); break; case CL_PLATFORM_HOST_TIMER_RESOLUTION: case CL_PLATFORM_COMMAND_BUFFER_CAPABILITIES_KHR: json.addScalar(infoString, *static_cast(data)); break; case CL_PLATFORM_EXTERNAL_MEMORY_IMPORT_HANDLE_TYPES_KHR: case CL_PLATFORM_SEMAPHORE_TYPES_KHR: case CL_PLATFORM_SEMAPHORE_IMPORT_HANDLE_TYPES_KHR: case CL_PLATFORM_SEMAPHORE_EXPORT_HANDLE_TYPES_KHR: json.addVector(infoString, std::vector((cl_uint *)data, (cl_uint *)data + size / sizeof(cl_uint))); break; default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetDeviceInfo: { cl::DeviceInfo info = call.params.getParamCaptures()[1].value.DeviceInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("device", clObject); switch (ToCLenum(info)) { case CL_DEVICE_IL_VERSION: case CL_DEVICE_LATEST_CONFORMANCE_VERSION_PASSED: case CL_DEVICE_OPENCL_C_VERSION: case CL_DEVICE_EXTENSIONS: case CL_DEVICE_VERSION: case CL_DEVICE_PROFILE: case CL_DRIVER_VERSION: case CL_DEVICE_VENDOR: case CL_DEVICE_NAME: json.addCString(infoString, static_cast(data)); break; case CL_DEVICE_TYPE: case CL_DEVICE_MAX_MEM_ALLOC_SIZE: case CL_DEVICE_LOCAL_MEM_SIZE: case CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE: case CL_DEVICE_GLOBAL_MEM_SIZE: case CL_DEVICE_GLOBAL_MEM_CACHE_SIZE: case CL_DEVICE_HALF_FP_CONFIG: case CL_DEVICE_SINGLE_FP_CONFIG: case CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES: case CL_DEVICE_ATOMIC_FENCE_CAPABILITIES: case CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES: case CL_DEVICE_SVM_CAPABILITIES: case CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES: case CL_DEVICE_PARTITION_AFFINITY_DOMAIN: case CL_DEVICE_DOUBLE_FP_CONFIG: case CL_DEVICE_QUEUE_ON_HOST_PROPERTIES: case CL_DEVICE_EXECUTION_CAPABILITIES: // cl_ulong and cl_bitfield json.addScalar(infoString, *static_cast(data)); break; case CL_DEVICE_VENDOR_ID: case CL_DEVICE_MAX_COMPUTE_UNITS: case CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: case CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR: case CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT: case CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT: case CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG: case CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT: case CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE: case CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF: case CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR: case CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT: case CL_DEVICE_NATIVE_VECTOR_WIDTH_INT: case CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG: case CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT: case CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE: case CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF: case CL_DEVICE_MAX_CLOCK_FREQUENCY: case CL_DEVICE_ADDRESS_BITS: case CL_DEVICE_IMAGE_SUPPORT: case CL_DEVICE_MAX_READ_IMAGE_ARGS: case CL_DEVICE_MAX_WRITE_IMAGE_ARGS: case CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS: case CL_DEVICE_PIPE_SUPPORT: case CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT: case CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT: case CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT: case CL_DEVICE_NUMERIC_VERSION: case CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS: case CL_DEVICE_MAX_NUM_SUB_GROUPS: case CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT: case CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT: case CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT: case CL_DEVICE_PIPE_MAX_PACKET_SIZE: case CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS: case CL_DEVICE_MAX_PIPE_ARGS: case CL_DEVICE_MAX_ON_DEVICE_EVENTS: case CL_DEVICE_MAX_ON_DEVICE_QUEUES: case CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE: case CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE: case CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT: case CL_DEVICE_IMAGE_PITCH_ALIGNMENT: case CL_DEVICE_PREFERRED_INTEROP_USER_SYNC: case CL_DEVICE_REFERENCE_COUNT: case CL_DEVICE_PARTITION_MAX_SUB_DEVICES: case CL_DEVICE_LINKER_AVAILABLE: case CL_DEVICE_HOST_UNIFIED_MEMORY: case CL_DEVICE_COMPILER_AVAILABLE: case CL_DEVICE_AVAILABLE: case CL_DEVICE_ENDIAN_LITTLE: case CL_DEVICE_ERROR_CORRECTION_SUPPORT: case CL_DEVICE_LOCAL_MEM_TYPE: case CL_DEVICE_MAX_CONSTANT_ARGS: case CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE: case CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE: case CL_DEVICE_MEM_BASE_ADDR_ALIGN: case CL_DEVICE_MAX_SAMPLERS: json.addScalar(infoString, *static_cast(data)); break; case CL_DEVICE_MAX_WORK_GROUP_SIZE: case CL_DEVICE_IMAGE2D_MAX_WIDTH: case CL_DEVICE_IMAGE2D_MAX_HEIGHT: case CL_DEVICE_IMAGE3D_MAX_WIDTH: case CL_DEVICE_IMAGE3D_MAX_HEIGHT: case CL_DEVICE_IMAGE3D_MAX_DEPTH: case CL_DEVICE_IMAGE_MAX_BUFFER_SIZE: case CL_DEVICE_IMAGE_MAX_ARRAY_SIZE: case CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE: case CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE: case CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE: case CL_DEVICE_PRINTF_BUFFER_SIZE: case CL_DEVICE_PROFILING_TIMER_RESOLUTION: case CL_DEVICE_MAX_PARAMETER_SIZE: json.addScalar(infoString, *static_cast(data)); break; case CL_DEVICE_MAX_WORK_ITEM_SIZES: json.addVector(infoString, std::vector((size_t *)data, (size_t *)data + size / sizeof(size_t))); break; case CL_DEVICE_PARTITION_TYPE: case CL_DEVICE_PARTITION_PROPERTIES: json.addVector(infoString, std::vector( (cl_ulong *)data, (cl_ulong *)data + size / sizeof(cl_ulong))); break; case CL_DEVICE_PARENT_DEVICE: { std::ostringstream voidStream; voidStream << static_cast(*(cl_device_id *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_DEVICE_PLATFORM: { std::ostringstream voidStream; voidStream << static_cast(*(cl_platform_id *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_DEVICE_ILS_WITH_VERSION: case CL_DEVICE_OPENCL_C_FEATURES: case CL_DEVICE_OPENCL_C_ALL_VERSIONS: case CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION: case CL_DEVICE_EXTENSIONS_WITH_VERSION: { const cl_name_version *nameVersion = static_cast(data); json.startGroup(infoString); for (size_t j = 0; j < size / sizeof(cl_name_version); ++j) { json.addScalar(nameVersion[j].name, nameVersion[j].version); } json.endGroup(); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetContextInfo: { cl::ContextInfo info = call.params.getParamCaptures()[1].value.ContextInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("context", clObject); switch (ToCLenum(info)) { case CL_PLATFORM_PROFILE: case CL_PLATFORM_VERSION: case CL_PLATFORM_NAME: case CL_PLATFORM_VENDOR: case CL_PLATFORM_EXTENSIONS: case CL_PLATFORM_ICD_SUFFIX_KHR: { json.addCString(infoString, static_cast(data)); break; } case CL_CONTEXT_REFERENCE_COUNT: case CL_CONTEXT_NUM_DEVICES: json.addScalar(infoString, *static_cast(data)); break; case CL_CONTEXT_PROPERTIES: json.addVector(infoString, std::vector( (cl_ulong *)data, (cl_ulong *)data + size / sizeof(cl_ulong))); break; case CL_CONTEXT_DEVICES: { const cl_device_id *devices = static_cast(data); std::vector devicesStrings; for (size_t j = 0; j < size / sizeof(cl_device_id); ++j) { std::ostringstream voidStream; voidStream << static_cast(devices[j]); devicesStrings.push_back(voidStream.str()); } json.addVectorOfStrings(infoString, devicesStrings); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetCommandQueueInfo: { cl::CommandQueueInfo info = call.params.getParamCaptures()[1].value.CommandQueueInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("command_queue", clObject); switch (ToCLenum(info)) { case CL_QUEUE_DEVICE_DEFAULT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_command_queue *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_QUEUE_CONTEXT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_context *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_QUEUE_DEVICE: { std::ostringstream voidStream; voidStream << static_cast(*(cl_device_id *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_QUEUE_REFERENCE_COUNT: case CL_QUEUE_SIZE: { json.addScalar(infoString, *static_cast(data)); break; } case CL_QUEUE_PROPERTIES: { json.addScalar(infoString, *static_cast(data)); break; } case CL_QUEUE_PROPERTIES_ARRAY: { json.addVector(infoString, std::vector( (cl_ulong *)data, (cl_ulong *)data + size / sizeof(cl_ulong))); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetProgramInfo: { cl::ProgramInfo info = call.params.getParamCaptures()[1].value.ProgramInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("program", clObject); switch (ToCLenum(info)) { case CL_PROGRAM_SOURCE: case CL_PROGRAM_IL: case CL_PROGRAM_KERNEL_NAMES: { json.addCString(infoString, static_cast(data)); break; } case CL_PROGRAM_CONTEXT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_context *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_PROGRAM_REFERENCE_COUNT: case CL_PROGRAM_NUM_DEVICES: case CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT: case CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT: { json.addScalar(infoString, *static_cast(data)); break; } case CL_PROGRAM_DEVICES: { const cl_device_id *devices = static_cast(data); std::vector devicesStrings; for (size_t j = 0; j < size / sizeof(cl_device_id); ++j) { std::ostringstream voidStream; voidStream << static_cast(devices[j]); devicesStrings.push_back(voidStream.str()); } json.addVectorOfStrings(infoString, devicesStrings); break; } case CL_PROGRAM_NUM_KERNELS: { json.addScalar(infoString, *static_cast(data)); break; } case CL_PROGRAM_BINARY_SIZES: json.addVector(infoString, std::vector((size_t *)data, (size_t *)data + size / sizeof(size_t))); break; case CL_PROGRAM_BINARIES: json.addVector(infoString, std::vector( (unsigned char *)data, (unsigned char *)data + size / sizeof(unsigned char))); break; default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetProgramBuildInfo: { cl::ProgramBuildInfo info = call.params.getParamCaptures()[2].value.ProgramBuildInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("program", clObject); cl_device_id device = call.params.getParamCaptures()[1].value.cl_device_idVal; std::ostringstream objectStream2; objectStream2 << static_cast(device); clObject = objectStream2.str(); json.addString("device", clObject); switch (ToCLenum(info)) { case CL_PROGRAM_BUILD_OPTIONS: case CL_PROGRAM_BUILD_LOG: { json.addCString(infoString, static_cast(data)); break; } case CL_PROGRAM_BINARY_TYPE: { json.addScalar(infoString, *static_cast(data)); break; } case CL_PROGRAM_BUILD_STATUS: { json.addScalar(infoString, *static_cast(data)); break; } case CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE: { json.addScalar(infoString, *static_cast(data)); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetKernelInfo: { cl::KernelInfo info = call.params.getParamCaptures()[1].value.KernelInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("kernel", clObject); switch (ToCLenum(info)) { case CL_KERNEL_FUNCTION_NAME: case CL_KERNEL_ATTRIBUTES: { json.addCString(infoString, static_cast(data)); break; } case CL_KERNEL_NUM_ARGS: case CL_KERNEL_REFERENCE_COUNT: { json.addScalar(infoString, *static_cast(data)); break; } case CL_KERNEL_CONTEXT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_context *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_KERNEL_PROGRAM: { std::ostringstream voidStream; voidStream << static_cast(*(cl_program *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetKernelArgInfo: { cl::KernelArgInfo info = call.params.getParamCaptures()[2].value.KernelArgInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("kernel", clObject); cl_uint index = call.params.getParamCaptures()[1].value.cl_uintVal; json.addScalar("arg_index", index); switch (ToCLenum(info)) { case CL_KERNEL_ARG_TYPE_NAME: case CL_KERNEL_ARG_NAME: { json.addCString(infoString, static_cast(data)); break; } case CL_KERNEL_ARG_ADDRESS_QUALIFIER: case CL_KERNEL_ARG_ACCESS_QUALIFIER: { json.addScalar(infoString, *static_cast(data)); break; } case CL_KERNEL_ARG_TYPE_QUALIFIER: { json.addScalar(infoString, *static_cast(data)); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetKernelWorkGroupInfo: { cl::KernelWorkGroupInfo info = call.params.getParamCaptures()[2].value.KernelWorkGroupInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("kernel", clObject); cl_device_id device = call.params.getParamCaptures()[1].value.cl_device_idVal; std::ostringstream objectStream2; objectStream2 << static_cast(device); clObject = objectStream2.str(); json.addString("device", clObject); switch (ToCLenum(info)) { case CL_KERNEL_LOCAL_MEM_SIZE: case CL_KERNEL_PRIVATE_MEM_SIZE: { json.addScalar(infoString, *static_cast(data)); break; } case CL_KERNEL_WORK_GROUP_SIZE: case CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE: { json.addScalar(infoString, *static_cast(data)); break; } case CL_KERNEL_GLOBAL_WORK_SIZE: case CL_KERNEL_COMPILE_WORK_GROUP_SIZE: { json.addVector(infoString, std::vector((size_t *)data, (size_t *)data + 3)); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetEventInfo: { cl::EventInfo info = call.params.getParamCaptures()[1].value.EventInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("event", clObject); switch (ToCLenum(info)) { case CL_EVENT_REFERENCE_COUNT: case CL_EVENT_COMMAND_TYPE: { json.addScalar(infoString, *static_cast(data)); break; } case CL_EVENT_COMMAND_EXECUTION_STATUS: { json.addScalar(infoString, *static_cast(data)); break; } case CL_EVENT_CONTEXT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_context *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_EVENT_COMMAND_QUEUE: { std::ostringstream voidStream; voidStream << static_cast(*(cl_command_queue *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetEventProfilingInfo: { cl::ProfilingInfo info = call.params.getParamCaptures()[1].value.ProfilingInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("event", clObject); switch (ToCLenum(info)) { case CL_PROFILING_COMMAND_QUEUED: case CL_PROFILING_COMMAND_SUBMIT: case CL_PROFILING_COMMAND_START: case CL_PROFILING_COMMAND_END: case CL_PROFILING_COMMAND_COMPLETE: { json.addScalar(infoString, *static_cast(data)); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetMemObjectInfo: { cl::MemInfo info = call.params.getParamCaptures()[1].value.MemInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("memObj", clObject); switch (ToCLenum(info)) { case CL_MEM_TYPE: case CL_MEM_MAP_COUNT: case CL_MEM_REFERENCE_COUNT: case CL_MEM_USES_SVM_POINTER: { json.addScalar(infoString, *static_cast(data)); break; } case CL_MEM_FLAGS: { json.addScalar(infoString, *static_cast(data)); break; } case CL_MEM_SIZE: case CL_MEM_OFFSET: { json.addScalar(infoString, *static_cast(data)); break; } case CL_MEM_HOST_PTR: { std::ostringstream voidStream; voidStream << data; std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_MEM_CONTEXT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_context *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_MEM_ASSOCIATED_MEMOBJECT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_mem *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } case CL_MEM_PROPERTIES: { json.addVector(infoString, std::vector( (cl_mem_properties *)data, (cl_mem_properties *)data + size / sizeof(cl_mem_properties))); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetImageInfo: { cl::ImageInfo info = call.params.getParamCaptures()[1].value.ImageInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("image", clObject); switch (ToCLenum(info)) { case CL_IMAGE_FORMAT: { json.startGroup(infoString); json.addScalar("image_channel_order", static_cast(data)->image_channel_order); json.addScalar( "image_channel_data_type", static_cast(data)->image_channel_data_type); json.endGroup(); break; } case CL_IMAGE_NUM_MIP_LEVELS: case CL_IMAGE_NUM_SAMPLES: { json.addScalar(infoString, *static_cast(data)); break; } case CL_IMAGE_ELEMENT_SIZE: case CL_IMAGE_ROW_PITCH: case CL_IMAGE_SLICE_PITCH: case CL_IMAGE_WIDTH: case CL_IMAGE_HEIGHT: case CL_IMAGE_DEPTH: case CL_IMAGE_ARRAY_SIZE: { json.addScalar(infoString, *static_cast(data)); break; } case CL_IMAGE_BUFFER: { std::ostringstream voidStream; voidStream << static_cast(*(cl_mem *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } default: // Not supported or cannot add to JSON file break; } break; } case EntryPoint::CLGetSamplerInfo: { cl::SamplerInfo info = call.params.getParamCaptures()[1].value.SamplerInfoVal; std::ostringstream oss; oss << info; std::string infoString = oss.str(); json.addString("image", clObject); switch (ToCLenum(info)) { case CL_SAMPLER_REFERENCE_COUNT: case CL_SAMPLER_NORMALIZED_COORDS: case CL_SAMPLER_ADDRESSING_MODE: case CL_SAMPLER_FILTER_MODE: { json.addScalar(infoString, *static_cast(data)); break; } case CL_SAMPLER_PROPERTIES: { json.addVector(infoString, std::vector( (cl_sampler_properties *)data, (cl_sampler_properties *)data + size / sizeof(cl_sampler_properties))); break; } case CL_SAMPLER_CONTEXT: { std::ostringstream voidStream; voidStream << static_cast(*(cl_context *)data); std::string memLoc = voidStream.str(); json.addCString(infoString, memLoc.c_str()); break; } default: // Not supported or cannot add to JSON file break; } break; } default: break; } json.endGroup(); mCLInfoJson += std::string(json.data()) + ",\n"; } void FrameCaptureShared::writeJSONCLGetInfo() { std::stringstream jsonFileNameStream; jsonFileNameStream << mOutDirectory << FmtCapturePrefix(kNoContextId, mCaptureLabel) << "_OpenCL_info.json"; std::string jsonFileName = jsonFileNameStream.str(); SaveFileHelper saveData(jsonFileName); saveData.write(reinterpret_cast(mCLInfoJson.c_str()), mCLInfoJson.length()); } void FrameCaptureShared::writeCppReplayIndexFilesCL() { // Ensure the last frame is written. This will no-op if the frame is already written. mReplayWriter.saveFrame(); { std::stringstream header; header << "#pragma once\n"; header << "\n"; header << "#define CL_NO_EXTENSION_PROTOTYPES\n"; header << "#include \n"; header << "#include \n"; header << "#include \"trace_fixture_cl.h\"\n"; std::string includes = header.str(); mReplayWriter.setHeaderPrologue(includes); } { std::stringstream source; source << "#include \"" << FmtCapturePrefix(kNoContextId, mCaptureLabel) << ".h\"\n"; source << "#include \"trace_fixture_cl.h\"\n"; std::string sourcePrologue = source.str(); mReplayWriter.setSourcePrologue(sourcePrologue); } { std::string proto = "void InitReplay(void)"; std::stringstream source; source << proto << "\n"; source << "{\n"; WriteInitReplayCallCL(mCompression, source, mCaptureLabel, 0, mReadBufferSize, mMaxCLParamsSize); source << "}\n"; mReplayWriter.addPrivateFunction(proto, std::stringstream(), source); } { std::string proto = "void ReplayFrame(uint32_t frameIndex)"; std::stringstream source; source << proto << "\n"; source << "{\n"; source << " switch (frameIndex)\n"; source << " {\n"; for (uint32_t frameIndex : mActiveFrameIndices) { source << " case " << frameIndex << ":\n"; source << " " << FmtReplayFunction(kNoContextId, FuncUsage::Call, frameIndex) << ";\n"; source << " break;\n"; } source << " default:\n"; source << " break;\n"; source << " }\n"; source << "}\n"; mReplayWriter.addPublicFunction(proto, std::stringstream(), source); } for (auto extFuncName : mExtFuncsAdded) { mReplayWriter.addStaticVariable(extFuncName + "_fn", extFuncName); } std::stringstream protoSetupStream; protoSetupStream << "void SetupFirstFrame(void)"; std::string protoSetup = protoSetupStream.str(); std::stringstream headerStreamSetup; std::stringstream bodyStreamSetup; WriteCppReplayFunctionWithPartsCL(ReplayFunc::SetupFirstFrame, mReplayWriter, mCaptureStartFrame, &mBinaryData, mCLSetupCalls, headerStreamSetup, bodyStreamSetup); mReplayWriter.addPublicFunction(protoSetup, headerStreamSetup, bodyStreamSetup); { std::string proto = "void ResetReplay(void)"; std::stringstream source; source << proto << "\n" << "{\n"; printCLResetObjs(source); source << "}\n"; mReplayWriter.addPublicFunction(proto, std::stringstream(), source); } { std::stringstream fnameStream; fnameStream << mOutDirectory << FmtCapturePrefix(kNoContextId, mCaptureLabel); std::string fnamePattern = fnameStream.str(); mReplayWriter.setFilenamePattern(fnamePattern); } mReplayWriter.saveIndexFilesAndHeader(); writeJSONCL(); writeJSONCLGetInfo(); } void FrameCaptureShared::writeMainContextCppReplayCL() { { std::stringstream header; header << "#include \"" << FmtCapturePrefix(kNoContextId, mCaptureLabel) << ".h\"\n"; header << "#include \"trace_fixture_cl.h\"\n"; std::string headerString = header.str(); mReplayWriter.setSourcePrologue(headerString); } uint32_t frameIndex = getReplayFrameIndex(); if (frameIndex == 1) { { std::string proto = "void SetupReplay(void)"; std::stringstream out; out << proto << "\n"; out << "{\n"; // Setup all of the shared objects. out << " InitReplay();\n"; out << "}\n"; mReplayWriter.addPublicFunction(proto, std::stringstream(), out); } } if (!mFrameCalls.empty()) { std::stringstream protoStream; protoStream << "void " << FmtReplayFunction(kNoContextId, FuncUsage::Prototype, mFrameIndex); std::string proto = protoStream.str(); std::stringstream headerStream; std::stringstream bodyStream; WriteCppReplayFunctionWithPartsCL(ReplayFunc::Replay, mReplayWriter, mFrameIndex, &mBinaryData, mFrameCalls, headerStream, bodyStream); mReplayWriter.addPrivateFunction(proto, headerStream, bodyStream); } { std::stringstream fnamePatternStream; fnamePatternStream << mOutDirectory << FmtCapturePrefix(kNoContextId, mCaptureLabel); std::string fnamePattern = fnamePatternStream.str(); mReplayWriter.setFilenamePattern(fnamePattern); } if (mFrameIndex == mCaptureEndFrame) { mReplayWriter.saveFrame(); } } } // namespace angle