dispatcher.cc revision 11435
19651SAndreas.Sandberg@ARM.com/* 29651SAndreas.Sandberg@ARM.com * Copyright (c) 2011-2015 Advanced Micro Devices, Inc. 39651SAndreas.Sandberg@ARM.com * All rights reserved. 49651SAndreas.Sandberg@ARM.com * 59651SAndreas.Sandberg@ARM.com * For use for simulation and test purposes only 69651SAndreas.Sandberg@ARM.com * 79651SAndreas.Sandberg@ARM.com * Redistribution and use in source and binary forms, with or without 89651SAndreas.Sandberg@ARM.com * modification, are permitted provided that the following conditions are met: 99651SAndreas.Sandberg@ARM.com * 109651SAndreas.Sandberg@ARM.com * 1. Redistributions of source code must retain the above copyright notice, 119651SAndreas.Sandberg@ARM.com * this list of conditions and the following disclaimer. 129651SAndreas.Sandberg@ARM.com * 139651SAndreas.Sandberg@ARM.com * 2. Redistributions in binary form must reproduce the above copyright notice, 149651SAndreas.Sandberg@ARM.com * this list of conditions and the following disclaimer in the documentation 159651SAndreas.Sandberg@ARM.com * and/or other materials provided with the distribution. 169651SAndreas.Sandberg@ARM.com * 179651SAndreas.Sandberg@ARM.com * 3. Neither the name of the copyright holder nor the names of its contributors 189651SAndreas.Sandberg@ARM.com * may be used to endorse or promote products derived from this software 199651SAndreas.Sandberg@ARM.com * without specific prior written permission. 209651SAndreas.Sandberg@ARM.com * 219651SAndreas.Sandberg@ARM.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 229651SAndreas.Sandberg@ARM.com * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 239651SAndreas.Sandberg@ARM.com * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 249651SAndreas.Sandberg@ARM.com * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 259651SAndreas.Sandberg@ARM.com * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 269651SAndreas.Sandberg@ARM.com * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 279651SAndreas.Sandberg@ARM.com * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 289651SAndreas.Sandberg@ARM.com * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 299651SAndreas.Sandberg@ARM.com * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 309651SAndreas.Sandberg@ARM.com * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 319651SAndreas.Sandberg@ARM.com * POSSIBILITY OF SUCH DAMAGE. 329651SAndreas.Sandberg@ARM.com * 339651SAndreas.Sandberg@ARM.com * Author: Brad Beckmann, Marc Orr 349651SAndreas.Sandberg@ARM.com */ 359651SAndreas.Sandberg@ARM.com 369651SAndreas.Sandberg@ARM.com 379651SAndreas.Sandberg@ARM.com#include "gpu-compute/dispatcher.hh" 389651SAndreas.Sandberg@ARM.com 399651SAndreas.Sandberg@ARM.com#include "cpu/base.hh" 4011793Sbrandon.potter@amd.com#include "debug/GPUDisp.hh" 419651SAndreas.Sandberg@ARM.com#include "gpu-compute/cl_driver.hh" 429651SAndreas.Sandberg@ARM.com#include "gpu-compute/cl_event.hh" 439651SAndreas.Sandberg@ARM.com#include "gpu-compute/shader.hh" 449651SAndreas.Sandberg@ARM.com#include "gpu-compute/wavefront.hh" 459651SAndreas.Sandberg@ARM.com#include "mem/packet_access.hh" 469651SAndreas.Sandberg@ARM.com 479651SAndreas.Sandberg@ARM.comGpuDispatcher *GpuDispatcher::instance = nullptr; 489651SAndreas.Sandberg@ARM.com 499651SAndreas.Sandberg@ARM.comGpuDispatcher::GpuDispatcher(const Params *p) 509651SAndreas.Sandberg@ARM.com : DmaDevice(p), _masterId(p->system->getMasterId(name() + ".disp")), 519651SAndreas.Sandberg@ARM.com pioAddr(p->pio_addr), pioSize(4096), pioDelay(p->pio_latency), 529651SAndreas.Sandberg@ARM.com dispatchCount(0), dispatchActive(false), cpu(p->cpu), 5312334Sgabeblack@google.com shader(p->shader_pointer), driver(p->cl_driver), tickEvent(this) 549651SAndreas.Sandberg@ARM.com{ 559651SAndreas.Sandberg@ARM.com shader->handshake(this); 569651SAndreas.Sandberg@ARM.com driver->handshake(this); 579651SAndreas.Sandberg@ARM.com 589651SAndreas.Sandberg@ARM.com ndRange.wg_disp_rem = false; 599651SAndreas.Sandberg@ARM.com ndRange.globalWgId = 0; 609651SAndreas.Sandberg@ARM.com 619651SAndreas.Sandberg@ARM.com schedule(&tickEvent, 0); 629651SAndreas.Sandberg@ARM.com 639651SAndreas.Sandberg@ARM.com // translation port for the dispatcher 649651SAndreas.Sandberg@ARM.com tlbPort = new TLBPort(csprintf("%s-port%d", name()), this); 659651SAndreas.Sandberg@ARM.com 669651SAndreas.Sandberg@ARM.com num_kernelLaunched 679651SAndreas.Sandberg@ARM.com .name(name() + ".num_kernel_launched") 689651SAndreas.Sandberg@ARM.com .desc("number of kernel launched") 699651SAndreas.Sandberg@ARM.com ; 709651SAndreas.Sandberg@ARM.com} 719651SAndreas.Sandberg@ARM.com 729651SAndreas.Sandberg@ARM.comGpuDispatcher *GpuDispatcherParams::create() 739651SAndreas.Sandberg@ARM.com{ 749651SAndreas.Sandberg@ARM.com GpuDispatcher *dispatcher = new GpuDispatcher(this); 759651SAndreas.Sandberg@ARM.com GpuDispatcher::setInstance(dispatcher); 769651SAndreas.Sandberg@ARM.com 779651SAndreas.Sandberg@ARM.com return GpuDispatcher::getInstance(); 789651SAndreas.Sandberg@ARM.com} 799651SAndreas.Sandberg@ARM.com 809651SAndreas.Sandberg@ARM.comvoid 819651SAndreas.Sandberg@ARM.comGpuDispatcher::serialize(CheckpointOut &cp) const 829651SAndreas.Sandberg@ARM.com{ 839651SAndreas.Sandberg@ARM.com Tick event_tick = 0; 849651SAndreas.Sandberg@ARM.com 859651SAndreas.Sandberg@ARM.com if (ndRange.wg_disp_rem) 869651SAndreas.Sandberg@ARM.com fatal("Checkpointing not supported during active workgroup execution"); 879651SAndreas.Sandberg@ARM.com 889651SAndreas.Sandberg@ARM.com if (tickEvent.scheduled()) 899651SAndreas.Sandberg@ARM.com event_tick = tickEvent.when(); 909651SAndreas.Sandberg@ARM.com 919651SAndreas.Sandberg@ARM.com SERIALIZE_SCALAR(event_tick); 929651SAndreas.Sandberg@ARM.com 939651SAndreas.Sandberg@ARM.com} 949651SAndreas.Sandberg@ARM.com 959651SAndreas.Sandberg@ARM.comvoid 969651SAndreas.Sandberg@ARM.comGpuDispatcher::unserialize(CheckpointIn &cp) 979651SAndreas.Sandberg@ARM.com{ 989651SAndreas.Sandberg@ARM.com Tick event_tick; 999651SAndreas.Sandberg@ARM.com 1009651SAndreas.Sandberg@ARM.com if (tickEvent.scheduled()) 1019651SAndreas.Sandberg@ARM.com deschedule(&tickEvent); 1029651SAndreas.Sandberg@ARM.com 1039651SAndreas.Sandberg@ARM.com UNSERIALIZE_SCALAR(event_tick); 1049651SAndreas.Sandberg@ARM.com 1059651SAndreas.Sandberg@ARM.com if (event_tick) 1069651SAndreas.Sandberg@ARM.com schedule(&tickEvent, event_tick); 1079651SAndreas.Sandberg@ARM.com} 1089651SAndreas.Sandberg@ARM.com 1099651SAndreas.Sandberg@ARM.comAddrRangeList 1109651SAndreas.Sandberg@ARM.comGpuDispatcher::getAddrRanges() const 1119651SAndreas.Sandberg@ARM.com{ 1129651SAndreas.Sandberg@ARM.com AddrRangeList ranges; 1139651SAndreas.Sandberg@ARM.com 1149651SAndreas.Sandberg@ARM.com DPRINTF(GPUDisp, "dispatcher registering addr range at %#x size %#x\n", 1159651SAndreas.Sandberg@ARM.com pioAddr, pioSize); 1169651SAndreas.Sandberg@ARM.com 1179651SAndreas.Sandberg@ARM.com ranges.push_back(RangeSize(pioAddr, pioSize)); 1189651SAndreas.Sandberg@ARM.com 1199651SAndreas.Sandberg@ARM.com return ranges; 1209651SAndreas.Sandberg@ARM.com} 1219651SAndreas.Sandberg@ARM.com 1229651SAndreas.Sandberg@ARM.comTick 1239651SAndreas.Sandberg@ARM.comGpuDispatcher::read(PacketPtr pkt) 1249651SAndreas.Sandberg@ARM.com{ 1259651SAndreas.Sandberg@ARM.com assert(pkt->getAddr() >= pioAddr); 1269651SAndreas.Sandberg@ARM.com assert(pkt->getAddr() < pioAddr + pioSize); 1279651SAndreas.Sandberg@ARM.com 1289651SAndreas.Sandberg@ARM.com int offset = pkt->getAddr() - pioAddr; 1299651SAndreas.Sandberg@ARM.com pkt->allocate(); 1309651SAndreas.Sandberg@ARM.com 1319651SAndreas.Sandberg@ARM.com DPRINTF(GPUDisp, " read register %#x size=%d\n", offset, pkt->getSize()); 1329651SAndreas.Sandberg@ARM.com 1339651SAndreas.Sandberg@ARM.com if (offset < 8) { 1349651SAndreas.Sandberg@ARM.com assert(!offset); 1359651SAndreas.Sandberg@ARM.com assert(pkt->getSize() == 8); 1369651SAndreas.Sandberg@ARM.com 1379651SAndreas.Sandberg@ARM.com uint64_t retval = dispatchActive; 1389651SAndreas.Sandberg@ARM.com pkt->set(retval); 1399651SAndreas.Sandberg@ARM.com } else { 1409651SAndreas.Sandberg@ARM.com offset -= 8; 1419651SAndreas.Sandberg@ARM.com assert(offset + pkt->getSize() < sizeof(HsaQueueEntry)); 1429651SAndreas.Sandberg@ARM.com char *curTaskPtr = (char*)&curTask; 1439651SAndreas.Sandberg@ARM.com 1449651SAndreas.Sandberg@ARM.com memcpy(pkt->getPtr<const void*>(), curTaskPtr + offset, pkt->getSize()); 1459651SAndreas.Sandberg@ARM.com } 1469651SAndreas.Sandberg@ARM.com 1479651SAndreas.Sandberg@ARM.com pkt->makeAtomicResponse(); 1489651SAndreas.Sandberg@ARM.com 1499651SAndreas.Sandberg@ARM.com return pioDelay; 1509651SAndreas.Sandberg@ARM.com} 1519651SAndreas.Sandberg@ARM.com 1529651SAndreas.Sandberg@ARM.comTick 1539651SAndreas.Sandberg@ARM.comGpuDispatcher::write(PacketPtr pkt) 1549651SAndreas.Sandberg@ARM.com{ 1559651SAndreas.Sandberg@ARM.com assert(pkt->getAddr() >= pioAddr); 1569651SAndreas.Sandberg@ARM.com assert(pkt->getAddr() < pioAddr + pioSize); 1579651SAndreas.Sandberg@ARM.com 1589651SAndreas.Sandberg@ARM.com int offset = pkt->getAddr() - pioAddr; 1599651SAndreas.Sandberg@ARM.com 1609651SAndreas.Sandberg@ARM.com#if TRACING_ON 1619651SAndreas.Sandberg@ARM.com uint64_t data_val = 0; 1629651SAndreas.Sandberg@ARM.com 1639651SAndreas.Sandberg@ARM.com switch (pkt->getSize()) { 1649651SAndreas.Sandberg@ARM.com case 1: 1659651SAndreas.Sandberg@ARM.com data_val = pkt->get<uint8_t>(); 1669651SAndreas.Sandberg@ARM.com break; 1679651SAndreas.Sandberg@ARM.com case 2: 1689651SAndreas.Sandberg@ARM.com data_val = pkt->get<uint16_t>(); 1699651SAndreas.Sandberg@ARM.com break; 1709651SAndreas.Sandberg@ARM.com case 4: 1719651SAndreas.Sandberg@ARM.com data_val = pkt->get<uint32_t>(); 17213787Sgambordr@oregonstate.edu break; 17313787Sgambordr@oregonstate.edu case 8: 17413787Sgambordr@oregonstate.edu data_val = pkt->get<uint64_t>(); 17513787Sgambordr@oregonstate.edu break; 17613787Sgambordr@oregonstate.edu default: 17713787Sgambordr@oregonstate.edu DPRINTF(GPUDisp, "bad size %d\n", pkt->getSize()); 17813787Sgambordr@oregonstate.edu } 17913787Sgambordr@oregonstate.edu 18013787Sgambordr@oregonstate.edu DPRINTF(GPUDisp, "write register %#x value %#x size=%d\n", offset, data_val, 18113787Sgambordr@oregonstate.edu pkt->getSize()); 18213787Sgambordr@oregonstate.edu#endif 18313787Sgambordr@oregonstate.edu if (!offset) { 1849651SAndreas.Sandberg@ARM.com static int nextId = 0; 1859651SAndreas.Sandberg@ARM.com 1869651SAndreas.Sandberg@ARM.com // The depends field of the qstruct, which was previously unused, is 1879651SAndreas.Sandberg@ARM.com // used to communicate with simulated application. 1889651SAndreas.Sandberg@ARM.com if (curTask.depends) { 1899651SAndreas.Sandberg@ARM.com HostState hs; 1909651SAndreas.Sandberg@ARM.com shader->ReadMem((uint64_t)(curTask.depends), &hs, 1919651SAndreas.Sandberg@ARM.com sizeof(HostState), 0); 1929651SAndreas.Sandberg@ARM.com 1939651SAndreas.Sandberg@ARM.com // update event start time (in nano-seconds) 1949651SAndreas.Sandberg@ARM.com uint64_t start = curTick() / 1000; 1959651SAndreas.Sandberg@ARM.com 1969651SAndreas.Sandberg@ARM.com shader->WriteMem((uint64_t)(&((_cl_event*)hs.event)->start), 1979651SAndreas.Sandberg@ARM.com &start, sizeof(uint64_t), 0); 1989651SAndreas.Sandberg@ARM.com } 1999651SAndreas.Sandberg@ARM.com 2009651SAndreas.Sandberg@ARM.com // launch kernel 2019651SAndreas.Sandberg@ARM.com ++num_kernelLaunched; 2029651SAndreas.Sandberg@ARM.com 2039651SAndreas.Sandberg@ARM.com NDRange *ndr = &(ndRangeMap[nextId]); 2049651SAndreas.Sandberg@ARM.com // copy dispatch info 2059651SAndreas.Sandberg@ARM.com ndr->q = curTask; 2069651SAndreas.Sandberg@ARM.com 2079651SAndreas.Sandberg@ARM.com // update the numDispTask polled by the runtime 2089651SAndreas.Sandberg@ARM.com accessUserVar(cpu, (uint64_t)(curTask.numDispLeft), 0, 1); 2099651SAndreas.Sandberg@ARM.com 2109651SAndreas.Sandberg@ARM.com ndr->numWgTotal = 1; 2119651SAndreas.Sandberg@ARM.com 2129651SAndreas.Sandberg@ARM.com for (int i = 0; i < 3; ++i) { 2139651SAndreas.Sandberg@ARM.com ndr->wgId[i] = 0; 2149651SAndreas.Sandberg@ARM.com ndr->numWg[i] = divCeil(curTask.gdSize[i], curTask.wgSize[i]); 2159651SAndreas.Sandberg@ARM.com ndr->numWgTotal *= ndr->numWg[i]; 2169651SAndreas.Sandberg@ARM.com } 2179651SAndreas.Sandberg@ARM.com 2189651SAndreas.Sandberg@ARM.com ndr->numWgCompleted = 0; 2199651SAndreas.Sandberg@ARM.com ndr->globalWgId = 0; 2209651SAndreas.Sandberg@ARM.com ndr->wg_disp_rem = true; 2219651SAndreas.Sandberg@ARM.com ndr->execDone = false; 2229651SAndreas.Sandberg@ARM.com ndr->addrToNotify = (volatile bool*)curTask.addrToNotify; 2239651SAndreas.Sandberg@ARM.com ndr->numDispLeft = (volatile uint32_t*)curTask.numDispLeft; 2249651SAndreas.Sandberg@ARM.com ndr->dispatchId = nextId; 2259651SAndreas.Sandberg@ARM.com ndr->curCid = pkt->req->contextId(); 2269651SAndreas.Sandberg@ARM.com DPRINTF(GPUDisp, "launching kernel %d\n",nextId); 2279651SAndreas.Sandberg@ARM.com execIds.push(nextId); 2289651SAndreas.Sandberg@ARM.com ++nextId; 2299651SAndreas.Sandberg@ARM.com 2309651SAndreas.Sandberg@ARM.com dispatchActive = true; 2319651SAndreas.Sandberg@ARM.com 2329651SAndreas.Sandberg@ARM.com if (!tickEvent.scheduled()) { 2339651SAndreas.Sandberg@ARM.com schedule(&tickEvent, curTick() + shader->ticks(1)); 2349651SAndreas.Sandberg@ARM.com } 2359651SAndreas.Sandberg@ARM.com } else { 2369651SAndreas.Sandberg@ARM.com // populate current task struct 2379651SAndreas.Sandberg@ARM.com // first 64 bits are launch reg 2389651SAndreas.Sandberg@ARM.com offset -= 8; 2399651SAndreas.Sandberg@ARM.com assert(offset < sizeof(HsaQueueEntry)); 2409651SAndreas.Sandberg@ARM.com char *curTaskPtr = (char*)&curTask; 2419651SAndreas.Sandberg@ARM.com memcpy(curTaskPtr + offset, pkt->getPtr<const void*>(), pkt->getSize()); 2429651SAndreas.Sandberg@ARM.com } 2439651SAndreas.Sandberg@ARM.com 2449651SAndreas.Sandberg@ARM.com pkt->makeAtomicResponse(); 2459651SAndreas.Sandberg@ARM.com 2469651SAndreas.Sandberg@ARM.com return pioDelay; 2479651SAndreas.Sandberg@ARM.com} 2489651SAndreas.Sandberg@ARM.com 2499651SAndreas.Sandberg@ARM.com 2509651SAndreas.Sandberg@ARM.comBaseMasterPort& 2519651SAndreas.Sandberg@ARM.comGpuDispatcher::getMasterPort(const std::string &if_name, PortID idx) 2529651SAndreas.Sandberg@ARM.com{ 2539651SAndreas.Sandberg@ARM.com if (if_name == "translation_port") { 2549651SAndreas.Sandberg@ARM.com return *tlbPort; 2559651SAndreas.Sandberg@ARM.com } 25611321Ssteve.reinhardt@amd.com 2579651SAndreas.Sandberg@ARM.com return DmaDevice::getMasterPort(if_name, idx); 258} 259 260void 261GpuDispatcher::exec() 262{ 263 int fail_count = 0; 264 265 // There are potentially multiple outstanding kernel launches. 266 // It is possible that the workgroups in a different kernel 267 // can fit on the GPU even if another kernel's workgroups cannot 268 DPRINTF(GPUDisp, "Launching %d Kernels\n", execIds.size()); 269 270 while (execIds.size() > fail_count) { 271 int execId = execIds.front(); 272 273 while (ndRangeMap[execId].wg_disp_rem) { 274 //update the thread context 275 shader->updateContext(ndRangeMap[execId].curCid); 276 277 // attempt to dispatch_workgroup 278 if (!shader->dispatch_workgroups(&ndRangeMap[execId])) { 279 // if we failed try the next kernel, 280 // it may have smaller workgroups. 281 // put it on the queue to rety latter 282 DPRINTF(GPUDisp, "kernel %d failed to launch\n", execId); 283 execIds.push(execId); 284 ++fail_count; 285 break; 286 } 287 } 288 // let's try the next kernel_id 289 execIds.pop(); 290 } 291 292 DPRINTF(GPUDisp, "Returning %d Kernels\n", doneIds.size()); 293 294 if (doneIds.size() && cpu) { 295 shader->hostWakeUp(cpu); 296 } 297 298 while (doneIds.size()) { 299 // wakeup the CPU if any Kernels completed this cycle 300 DPRINTF(GPUDisp, "WorkGroup %d completed\n", doneIds.front()); 301 doneIds.pop(); 302 } 303} 304 305void 306GpuDispatcher::notifyWgCompl(Wavefront *w) 307{ 308 int kern_id = w->kern_id; 309 DPRINTF(GPUDisp, "notify WgCompl %d\n",kern_id); 310 assert(ndRangeMap[kern_id].dispatchId == kern_id); 311 ndRangeMap[kern_id].numWgCompleted++; 312 313 if (ndRangeMap[kern_id].numWgCompleted == ndRangeMap[kern_id].numWgTotal) { 314 ndRangeMap[kern_id].execDone = true; 315 doneIds.push(kern_id); 316 317 if (ndRangeMap[kern_id].addrToNotify) { 318 accessUserVar(cpu, (uint64_t)(ndRangeMap[kern_id].addrToNotify), 1, 319 0); 320 } 321 322 accessUserVar(cpu, (uint64_t)(ndRangeMap[kern_id].numDispLeft), 0, -1); 323 324 // update event end time (in nano-seconds) 325 if (ndRangeMap[kern_id].q.depends) { 326 HostState *host_state = (HostState*)ndRangeMap[kern_id].q.depends; 327 uint64_t event; 328 shader->ReadMem((uint64_t)(&host_state->event), &event, 329 sizeof(uint64_t), 0); 330 331 uint64_t end = curTick() / 1000; 332 333 shader->WriteMem((uint64_t)(&((_cl_event*)event)->end), &end, 334 sizeof(uint64_t), 0); 335 } 336 } 337 338 if (!tickEvent.scheduled()) { 339 schedule(&tickEvent, curTick() + shader->ticks(1)); 340 } 341} 342 343void 344GpuDispatcher::scheduleDispatch() 345{ 346 if (!tickEvent.scheduled()) 347 schedule(&tickEvent, curTick() + shader->ticks(1)); 348} 349 350void 351GpuDispatcher::accessUserVar(BaseCPU *cpu, uint64_t addr, int val, int off) 352{ 353 if (cpu) { 354 if (off) { 355 shader->AccessMem(addr, &val, sizeof(int), 0, MemCmd::ReadReq, 356 true); 357 val += off; 358 } 359 360 shader->AccessMem(addr, &val, sizeof(int), 0, MemCmd::WriteReq, true); 361 } else { 362 panic("Cannot find host"); 363 } 364} 365 366GpuDispatcher::TickEvent::TickEvent(GpuDispatcher *_dispatcher) 367 : Event(CPU_Tick_Pri), dispatcher(_dispatcher) 368{ 369} 370 371void 372GpuDispatcher::TickEvent::process() 373{ 374 dispatcher->exec(); 375} 376 377const char* 378GpuDispatcher::TickEvent::description() const 379{ 380 return "GPU Dispatcher tick"; 381} 382 383// helper functions for driver to retrieve GPU attributes 384int 385GpuDispatcher::getNumCUs() 386{ 387 return shader->cuList.size(); 388} 389 390void 391GpuDispatcher::setFuncargsSize(int funcargs_size) 392{ 393 shader->funcargs_size = funcargs_size; 394} 395