我有一个使用LWJGL实现的3D引擎,主要目的是渲染一个大型的世界' - 地形,植被,动画生物,天气,昼/夜循环等。引擎管理渲染所需的资源,如各种纹理,网格,模型等。
当玩家导航这个世界时,引擎会缓存不再需要的各种类型的数据丢弃/释放资源,并按需加载/缓存新需要的数据。这由一组后台线程处理,这些后台线程执行I / O任务(加载图像,加载新的地形' chunk',构建模型网格等)以及要在OpenGL上执行的任务队列上下文(分配新纹理,将数据上传到VBO,删除纹理等)。
除了具有依赖关系的任务之外,这一切都很好用。我将举例说明 - 让我们说引擎需要将新纹理应用于场景中的对象。
前两个是相对简单的,最后一个是我正在努力的 - 我似乎无法想出一个体面的设计,允许可重复使用和相对原子的任务当存在任务间依赖时,将它们链接在一起。
interface Task implements Runnable { TaskQueue getQueue(); } // Generic load-an-image task class LoadImageTask implements Task { private final String path; private BufferedImage image; public LoadImageTask( String path ) { this.path = path; } TaskQueue getQueue() { return TaskQueue.BACKGROUND; } public void run() { // load the image from the given location } } // Uploads an image to a given texture class UploadTextureTask implements Task { private BufferedImage image; private Texture texture; ... TaskQueue getQueue() { return TaskQueue.RENDER_THREAD; } public void run() { texture.buffer( image ); } } // Example task manager for the scenario outlined above class Example extends TaskManager { ... // Load the texture image final LoadImageTask loadImage = new LoadImageTask( ... ); add( loadImage ); // Allocate a texture final AllocateTextureTask allocate = ... add( allocate ); // Upload texture image final UploadTextureTask upload = ... add( upload, loadImage, allocate ); }
add
中的TaskManager
方法在后台注册相关队列上的任务。每个任务完成后,经理都会收到通知。当所有当前任务完成时(在这种情况下为loadImage
和allocate
),经理将启动序列中的下一个任务。
请注意,对add
任务的最终upload
调用会告诉管理员它对load
和allocate
任务有依赖性。
这个问题是关于资源'由生产者'返回的任务(例如LoadImageTask
)传递给'消费者'任务(例如UploadTextureTask
)当这些类没有以任何方式耦合时?
一种(垃圾)方式是使用一些自定义代码,在该任务完成时调用loadImage.getImage()
并将其传递给'链。使用uploadTask.setImage()
,但如果必须为每个用例编写大致相同的任务管理代码,这几乎使整个方法毫无意义。
我尝试使用通用getter / setter方法定义使用者和 producer 接口,并链接具有正确匹配数据类型的相关任务,但如果a任务消耗多个资源(如上面的上传任务)。
我还考虑过使用Future
和Callable
(后台任务队列无论如何都使用Executor
线程池)但它们实际上是针对阻塞整个线程而不是资源管理SE。此系统中的任务数量可以是数千个,其中大多数是当前正在运行或正在排队的待处理任务。阻止尚未启动的任务似乎毫无意义。另外,了解未来何时完成的唯一方法是通过调用isDone
来调用已完成任务的另一层复杂性。
我已经在这里以及OpenGL开发者和游戏网站上研究过这个问题但是没有遇到任何好的设计 - 它们似乎只是由剪切和粘贴代码或某种共享状态组成(通常是到处都有很多令人讨厌的铸造。也许我不是在寻找正确的条款?或者也许我的整个方法都是垃圾?
有没有人实施或遇到类似的东西?欢迎提出任何建议,批评,指示。 (并为墙上的文字道歉)
答案 0 :(得分:0)
Java 8中的一些新功能似乎可以解决这类问题,例如: CompletableFuture
,它使用Executor
s,Runnable
任务等与上述现有设计很好地集成。